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)