🛤️ Ruby on Rails - Callbacks
Updated at 2015-08-18 22:34
ActiveRecord
have very good support for binding callbacks to different model events.
Callback registration. Callback methods should be defined as protected or private.
# Block syntax
class User < ActiveRecord::Base
after_find do |user|
puts "You have found an object!"
end
end
# Method syntax
class User < ActiveRecord::Base
validates :login, :email, presence: true
before_validation :ensure_login_has_a_value
protected
def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
end
end
end
Register only to certain action context.
class User < ActiveRecord::Base
before_validation :normalize_name, on: :create
# :on takes an array as well
after_validation :set_location, on: [ :create, :update ]
protected
def normalize_name
self.name = self.name.downcase.titleize
end
def set_location
self.location = LocationService.query(self)
end
end
List of available events:
# Creating an object
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback
# Modifying an object
before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit/after_rollback
# Removing an object
before_destroy
around_destroy
after_destroy
after_commit/after_rollback
# Other callbacks
after_find
after_initialize
after_touch
Callbacks work with defined relations.
class User < ActiveRecord::Base
has_many :articles, dependent: :destroy
end
class Article < ActiveRecord::Base
after_destroy :log_destroy_action
def log_destroy_action
puts 'Article destroyed'
end
end
user = User.new
user.articles.create!
user.destroy
# both the user and the article are destroyed
Conditional callbacks. Callback won't be called if method with the given name returns false.
# Symbol for the predicate method to call.
class Order < ActiveRecord::Base
before_save :normalize_card_number, if: :paid_with_card?
end
# Bound inline block.
class Order < ActiveRecord::Base
before_save :normalize_card_number, if: Proc.new { |o| o.paid_with_card? }
end
Use callback classes to reuse common callbacks.
class PictureFileCallbacks
def self.after_destroy(picture_file)
if File.exist?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end
class PictureFile < ActiveRecord::Base
after_destroy PictureFileCallbacks
end
Callbacks can be used with transactions.
PictureFile.transaction do
picture_file_1.destroy
picture_file_2.save!
end
class PictureFile < ActiveRecord::Base
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