Custom validation in Rails
Here’s a little trick I like to do when I’m addressing validations in my models. Let’s say you have a standard Post
model. It might look something like the following.
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.integer "status"
t.datetime "published_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
If you’re like me and prone to ideas popping into your head at inopportune times then you probably only have a few seconds to jot down an idea before you’re off to the next thing. So I tend to set up validations to facilitate my craziness.
# /app/models/post.rb
class Post < ApplicationRecord
validates :title, presence: true
validates_uniqueness_of :title, case_sensitive: false
validate :body_on_publish
end
At first you might think to validate presence of the :body
because: why would you ever publish a post without content? Well, if you’re chasing a two-year-old around who’s about to paint the dog pink then right now might not be the time to flesh out a post. You can only fill in the :title
field and hit save before you have a pink dog.
So, we create a custom validation to solve this problem. The validate :body_on_publish
is going to reference a private method where we can add a little logic.
# /app/models/post.rb
class Post < ApplicationRecord
...
private
def body_on_publish
if status == 'published' && body.blank?
errors.add(:body, "can't be blank")
end
end
end
This allows us to only perform a presence check if we’re actually about to publish our post. :status
is simply an enum with ‘published’ as one of its values.
Now we can jot down a quick title as a note for later and only get bugged by a validation error if we’re actually trying to make something live.
Here’s the complete Post
model for your convenience.
class Post < ApplicationRecord
validates :title, presence: true
validates_uniqueness_of :title, case_sensitive: false
validate :body_on_publish
enum status: {
draft: 0,
published: 1
}
private
def body_on_publish
if status == 'published' && body.blank?
errors.add(:body, "can't be blank")
end
end
end