Ruby on Rails ActiveRecord Callbacks
Eapen Zacharias
Posted on July 5, 2022
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 before
, around
, after
.
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
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 forafter_commit :hook, on: :create
-
after_destroy_commit
: Shortcut forafter_commit :hook, on: :destroy
-
after_save_commit
: Shortcut forafter_commit :hook, on: [ :create, :update ]
-
after_update_commit
: Shortcut forafter_commit :hook, on: :update
-
-
after_rollback
This callback is called after a create, update, or destroy are rolled back. Note that all options ofafter_commit
works forafter_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
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
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
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
# For negative conditional you can use unless
class User < ApplicationRecord
validates :first_name, presence: true, unless: Proc.new { |user| user.last_name.present? }
end
# 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
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
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
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!
Posted on July 5, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.