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
Written by Matt Haliski

The First of His Name, Consumer of Tacos, Operator of Computers, Mower of Grass, Father of the Unsleeper, King of Bad Function Names, Feeder of AI Overlords.