ActiveModel::Dirty is responsible for tracking the changes to attributes in an active model. This often comes in handy with ActiveRecord callbacks to perform certain actions only if some attribute’s value changed.

Imagine we have a Comment model which is flagged edited whenever its content changes:

class Comment < ActiveRecord::Base
  before_update :set_edited, if: content_changed?

  validates :content, presence: true

  def set_edited
    write_attribute(:edited, true)
  end
end

ActiveModel defines magic methods for each attribute to see if they changed. For content, it’s content_changed?. The content attribute is now dirty. Whenever the record is saved, the new content is persisted to the database.

A new requirement comes up. We have to store the previous value of the comment in the previous_content attribute if the comment has been edited. Luckily Rails makes this a breeze:

class Comment < ActiveRecord::Base
  before_update :set_edited, :set_previous_content, if: content_changed?

  validates :content, presence: true

  def set_edited
    write_attribute(:edited, true)
  end

  def set_previous_content
    write_attribute(:previous_content, content_was)
  end
end

Note how content_was is called. Like content_changed? this is another magic method in ActiveModel::Dirty. It returns the previous value of content, or the current value if the content hasn’t been changed. No need to store the old value inside some instance variable, the dirty work has already been done for you!