DEV Community

Cover image for Ruby on Rails ActiveRecord Callbacks
Eapen Zacharias
Eapen Zacharias

Posted on

Ruby on Rails ActiveRecord Callbacks

Callbacks are a way that ActiveRecord gives us to intervene in an object's life cycle.
It's almost like it pauses before or after key moments in its internal processes, to check in with you to see if you have code customization that you'd like to run before it continues. It allows you then to run code automatically before or after an Active Record object is created, saved, updated, deleted, validated, or loaded from the database.

We could use this callbacks to perform various tasks:

  • Set values for the current record
  • Create, update or destroy other records
  • Perform cleanup and housekeeping tasks
  • Log information
  • Send an email or make an API call

The order of callbacks is beforearoundafter.

before: happens before the action happens

around: can have logic before and after the action being run. The code before yield is invoked before the action and the code after is called after the action

example:

around_create :method_name

def method_name
  logger.info('Before action code')
  yield
  logger.info('After action this will run')
end
Enter fullscreen mode Exit fullscreen mode

after : runs after an action is complete

Here is a list of Rails inbuilt Active Record Callback methods:

Creating an Object

  • before_validation Defines a callback that will get called right before validation.
  • after_validation Defines a callback that will get called right after validation.
  • before_save Registers a callback to be called before a record is saved.
  • around_save Registers a callback to be called around the save of a record.
  • before_create Registers a callback to be called before a record is created.
  • around_create Registers a callback to be called around the creation of a record.
  • after_create Registers a callback to be called after a record is created.
  • after_save Registers a callback to be called after a record is saved.
  • after_commit This callback is called after a record has been created, updated, or destroyed. Example: after_commit :do_baz, on: :destroy after_commit :do_bar_baz, on: [:update, :destroy]
    • after_create_commit: Shortcut for after_commit :hook, on: :create
    • after_destroy_commit: Shortcut for after_commit :hook, on: :destroy
    • after_save_commit: Shortcut for after_commit :hook, on: [ :create, :update ]
    • after_update_commit: Shortcut for after_commit :hook, on: :update
  • after_rollback This callback is called after a create, update, or destroy are rolled back. Note that all options of after_commit works for after_rollback too.

Updating an Object

  • before_validation
  • after_validation
  • before_save
  • around_save
  • before_update Registers a callback to be called before a record is updated.
  • around_update Registers a callback to be called around the update of a record.
  • after_update Registers a callback to be called after a record is updated.
  • after_save
  • after_commit / after_rollback

Destroying an Object

  • before_destroy Registers a callback to be called before a record is destroyed.
  • around_destroy Registers a callback to be called around the destruction of a record.
  • after_destroy Registers a callback to be called after a record is destroyed.
  • after_commit / after_rollback

Use Callbacks to Automate Actions

In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks:

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

  before_validation :ensure_login_has_a_value

  private
    def ensure_login_has_a_value
      if login.nil?
        self.login = email unless email.blank?
      end
    end
end
Enter fullscreen mode Exit fullscreen mode

The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line:

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

  before_create do
    self.name = login.capitalize if name.blank?
  end
end
Enter fullscreen mode Exit fullscreen mode

Callbacks can also be registered to only fire on certain life cycle events:

class User < ApplicationRecord
  before_validation :normalize_name, on: :create

  # :on takes an array as well
  after_validation :set_location, on: [ :create, :update ]

  private
    def normalize_name
      self.name = name.downcase.titleize
    end

    def set_location
      self.location = LocationService.query(self)
    end
end
Enter fullscreen mode Exit fullscreen mode

Conditional Callbacks

By default callbacks will run all the time at the point at which you've registered them to execute. But we can also execute callbacks conditionally. You can tell your model to execute callback methods only when a particular condition is met.

We can do this by using the following options:

  • :if
  • :unless

Here are few examples:

class User < ApplicationRecord
  validates :name, presence: true, if: :admin?

  def admin?
    conditional here that returns boolean value
  end
end
Enter fullscreen mode Exit fullscreen mode
# For negative conditional you can use unless
class User < ApplicationRecord
  validates :first_name, presence: true, unless: Proc.new { |user| user.last_name.present? }
end
Enter fullscreen mode Exit fullscreen mode
# You can also pass a string, which will be executed via instance_eval
class User < ApplicationRecord
  validates :first_name, presence: true, if: 'last_name.blank?'
end
Enter fullscreen mode Exit fullscreen mode

Skipping Callbacks

Every now and then it's useful to be able to skip callbacks specially when we just have to update .

The first way is that if you have an instance of an object and you want to update it, you can call update_column or update_columns, or if you want to get rid of an object without having any callbacks, you can call delete. Normally the method we use is destroy. Destroy does activate our callbacks, but delete does not. The reason these three methods skip our callbacks is because all three of them, construct SQL to submit directly to the database.
Usage:

instance.update_column(name, value)
instance.update_columns(name => value, name => value)
instance.delete
Enter fullscreen mode Exit fullscreen mode

The other way we skip callback is if we have an ActiveRecord relation. We can either call update_all or delete_all on those relations.

Usage:

relation.update_all
# example:
Product.where("modified_at < ?", 2.years.ago).update_all(active: false)

relation.delete_all
# example:
Product.where("created_at < ?", 5.years.ago).delete_all
Enter fullscreen mode Exit fullscreen mode

Note: You can also use conditional callbacks to skip a callback for part of the business logic of your application.

Thanks For Reading, Follow Me For More
Share your suggestions, doubts and feedback in the comments section!

Top comments (0)