Active Record Callbacks


Is a part of Active Record

Registering a Callback

Macro-style class method

class User < ApplicationRecord
  validates :username, :email, presence: true

  before_validation :ensure_username_has_value

  private
    def ensure_username_has_value
      if username.blank?
        self.username = email
      end
    end
end

or

class User < ApplicationRecord
  validates :username, :email, presence: true

  before_validation do
    self.username = email if username.blank?
  end
end

Pass in a Proc

class User < ApplicationRecord
  validates :username, :email, presence: true

  before_validation ->(user) { user.username = user.email if user.username.blank? }
end

Custom callback Object

class User < ApplicationRecord
  validates :username, :email, presence: true

  before_validation AddUsername
end

class AddUsername
  def self.before_validation(record)
    if record.username.blank?
      record.username = record.email
    end
  end
end

Register callback on Lifecycle events

before_validation :ensure_username_has_value, on: :create

see also Knowledge/test/Active Record Validations#On

Available Callbacks

Triggered when Creating an Object

before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit / after_rollback

Triggered when Updating an Object

before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit / after_rollback

Triggered when Destroying an Object

before_destroy
around_destroy
after_destroy
after_commit / after_rollback

Others

after_initialize
after_find
after_touch

Conditional Callbacks

Like in validations you can add conditions to your callbacks.

before_save :normalize_card_number, if: :paid_with_card?

before_save :normalize_card_number,
    if: ->(order) { order.paid_with_card? }
    
before_save :normalize_card_number, if: -> { paid_with_card? }

Association Callbacks

Triggered by associations

before_add
after_add
before_remove
after_remove
class Author < ApplicationRecord
  has_many :books, before_add: :check_limit

  private
    def check_limit(_book)
      if books.count >= 5
        errors.add(:base, "Cannot add more than 5 books for this author")
        throw(:abort)
      end
    end
end

Transaction Callbacks

class PictureFile < ApplicationRecord
  after_commit :delete_picture_file_from_disk, on: :destroy

  def delete_picture_file_from_disk
    if File.exist?(filepath)
      File.delete(filepath)
    end
  end
end

In This example the physical file associated with the model only gets removed after the successful destruction of the object.

Aliases for specific commits

after_destroy_commit
after_create_commit
after_update_commit
after_save_commit

then you can leave out the on: