With our routes, controllers, and views all converted to use Rails' conventions and conveniences, we have one last big piece to change: our models.
What we're going to do next feels like a bit of magic - we're just going to let our models Post
and Comment
inherit from ActiveRecord::Base
and remove all the code:
### app/models/comment.rb ###
class Comment < ActiveRecord::Base
end
### app/models/post.rb ###
class Post < ActiveRecord::Base
end
That's right, we can get rid of the entirety of the content of our classes now that we're inheriting from ActiveRecord::Base
. Remember how we were sharing some functionality between our models using BaseModel
earlier? Well, ActiveRecord::Base
provides all of the functionality we've been using thus far, using (mostly) the same method names.
We went through this process mostly to show you what ActiveRecord::Base
actually does under the hood - you can always check your original models to understand what Rails provides to you as a framework that you don't have to implement yourself.
When we use Active Record based models, and we follow the convention like below:
Model Name | File Location and Name | Database Table Name |
---|---|---|
Post | app/models/post.rb | posts |
MountainBike | app/models/mountain_bike.rb | mountain_bikes |
Rails will automatically map a model to a database table if you follow this naming convention. Inheriting from ActiveRecord::Base
will allow you to access data records in the database directly through your models:
### find a post, read its attributes ###
post = Post.find 1
# Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1 [["id", 1]]
#=> #<Post:0x00000007360e78
# id: 1,
# title: "Blog 1",
# body: "Lorem ipsum dolor sit amet.",
# author: "Brad",
# created_at: Sun, 07 Dec 2014 00:00:00 UTC +00:00>
post.title
# => "Blog 1"
post.body
# => "Lorem ipsum dolor sit amet."
post.created_at
# => Sun, 07 Dec 2014 00:00:00 UTC +00:00
post.title="My Blog"
# => "My Blog"
post.save
# => true
We have our Active Record based models set up, but they don't have validations ready yet:
### check validity of empty post ###
post = Post.new
# => #<Post:0x0000000252fbc8
# id: nil,
# title: nil,
# body: nil,
# author: nil,
# created_at: nil>
post.valid?
# => true
post.errors
# => #<ActiveModel::Errors:0x00000003664bc8
# @base=
# #<Post:0x0000000252fbc8
# id: nil,
# title: nil,
# body: nil,
# author: nil,
# created_at: nil>,
# @messages={}>
Rails allows you to define validations in a very easy way:
### app/models/post.rb ###
class Post < ActiveRecord::Base
validates_presence_of :title, :body, :author
end
### app/models/comment.rb ###
class Comment < ActiveRecord::Base
validates_presence_of :body, :author
end
Let's take a look how this works:
### Post validation ###
post = Post.new
# => #<Post:0x000000074be7e8
# id: nil,
# title: nil,
# body: nil,
# author: nil,
# created_at: nil>
post.valid?
# => false
post.errors.messages
# => {:title=>["can't be blank"],
# :body=>["can't be blank"],
# :author=>["can't be blank"]}
### Comment validation ###
comment = Comment.new
# => #<Comment:0x00000007676dd8
# id: nil,
# body: nil,
# author: nil,
# post_id: nil,
# created_at: nil>
comment.valid?
# => false
comment.errors.messages
# => {:body=>["can't be blank"], :author=>["can't be blank"]}
Validation impacts on a record's persistence:
post = Post.new
# => #<Post:0x00000005aaece0
# id: nil,
# title: nil,
# body: nil,
# author: nil,
# created_at: nil,
# updated_at: nil>
post.valid?
# => false
# as expected, this invalid instance returns false here
post.save
# (0.3ms) begin transaction
# (0.0ms) rollback transaction
# => false
# but if we call the "bang" (!) version of the method...
post.save!
# (0.2ms) begin transaction
# (0.0ms) rollback transaction
# ActiveRecord::RecordInvalid: Validation failed: Title can't be blank, Body can't be blank, Author can't be blank
# it throws an error. this convention repeats throughout Rails.
# with `.create` for example...
Post.create({title:'foo', body:'bar', author:'baz'})
# (0.1ms) begin transaction
# SQL (0.2ms) INSERT INTO "posts" ("title", "body", "author", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["title", "foo"], ["body", "bar"], ["author", "baz"], ["created_at", "2015-01-31 10:59:33.931414"], ["updated_at", "2015-01-31 10:59:33.931414"]]
# (13.1ms) commit transaction
# => #<Post:0x0000000537a870 id: 4, title: "foo", body: "bar", author: "baz", ...>
Post.create({})
# (0.1ms) begin transaction
# (0.1ms) rollback transaction
#=> #<Post:0x0000000553c820
# id: nil,
# title: nil,
# body: nil,
# author: nil,
# created_at: nil,
# updated_at: nil>
# with create, we always get back a `Post` object
# but with invalid attributes sent to `.create!`
Post.create!({})
# (0.1ms) begin transaction
# (0.1ms) rollback transaction
# ActiveRecord::RecordInvalid: Validation failed: Title can't be blank, Body can't be blank, Author can't be blank
# we get an error instead
Other than validate_presence_of
, there are other validations that you can use with Rails. Here are some other validations that you may often use:
There other ways of defining validations as well. We may use the validates
method,
and with that method we can specify which attributes to validate and what to validate.
For instance,
validates :name, presence: true
validates the name attribute by checking that it is present before saving. When using validations in this form, we pass the types of validation as k-v pairs of a hash.
validates :name, presence: true, uniqueness: true
By adding uniqueness: true
, we may also check that a name is unique along with checking
that it is present in our model.
There's even more to learn about validations, but we won't be covering it here. For more information on validations, check out the Rails documentation and the Rails guides page.
Callbacks are methods that "hook" into the lifecycle of an Active Record object. They may be used to run logic whenever an object is created, saved, updated, deleted, validated, or loaded from the database. Here is a list of available callbacks that may be used in Rails:
after_initialize
after_find
after_touch
before_validation
, after_validation
before_save
, around_save
, after_save
before_create
, around_create
, after_create
before_update
, around_update
, after_update
,
before_destroy
, around_destroy
, after_destroy
after_commit
after_rollback
For more information on Active Record callbacks, you may refer to the Ruby on Rails API page or the Rails guide on Active Record Callbacks.