module ActiveModel::Callbacks

Active Model Callbacks ↑

Provides an interface for any class to have Active Record like callbacks.

Like the Active Record methods, the callback chain is aborted as soon as one of the methods throws :abort.

First, extend ActiveModel::Callbacks from the class you are creating:

class MyModel
  extend ActiveModel::Callbacks
end

Then define a list of methods that you want callbacks attached to:

define_model_callbacks :create, :update

This will provide all three standard callbacks (before, around and after) for both the :create and :update methods. To implement, you need to wrap the methods you want callbacks on in a block so that the callbacks get a chance to fire:

def create
  run_callbacks :create do
    # Your create action methods here
  end
end

Then in your class, you can use the before_create, after_create, and around_create methods, just as you would in an Active Record model.

before_create :action_before_create

def action_before_create
  # Your code here
end

When defining an around callback remember to yield to the block, otherwise it won’t be executed:

around_create :log_status

def log_status
  puts 'going to call the block...'
  yield
  puts 'block successfully called.'
end

You can choose to have only specific callbacks by passing a hash to the define_model_callbacks method.

define_model_callbacks :create, only: [:after, :before]

Would only create the after_create and before_create callback methods in your class.

NOTE: Calling the same callback multiple times will overwrite previous callback definitions.

Public Instance Methods

define_model_callbacks(*callbacks) click to toggle source

define_model_callbacks accepts the same options define_callbacks does, in case you want to overwrite a default. Besides that, it also accepts an :only option, where you can choose if you want all types (before, around or after) or just some.

define_model_callbacks :initializer, only: :after

Note, the only: <type> hash will apply to all callbacks defined on that method call. To get around this you can call the define_model_callbacks method as many times as you need.

define_model_callbacks :create,  only: :after
define_model_callbacks :update,  only: :before
define_model_callbacks :destroy, only: :around

Would create after_create, before_update, and around_destroy methods only.

You can pass in a class to before_<type>, after_<type> and around_<type>, in which case the callback will call that class’s <action>_<type> method passing the object that the callback is being called on.

class MyModel
  extend ActiveModel::Callbacks
  define_model_callbacks :create

  before_create AnotherClass
end

class AnotherClass
  def self.before_create( obj )
    # obj is the MyModel instance that the callback is being called on
  end
end

NOTE: method_name passed to define_model_callbacks must not end with !, ? or =.

# File lib/active_model/callbacks.rb, line 109
def define_model_callbacks(*callbacks)
  options = callbacks.extract_options!
  options = {
    skip_after_callbacks_if_terminated: true,
    scope: [:kind, :name],
    only: [:before, :around, :after]
  }.merge!(options)

  types = Array(options.delete(:only))

  callbacks.each do |callback|
    define_callbacks(callback, options)

    types.each do |type|
      send("_define_#{type}_model_callback", self, callback)
    end
  end
end

Private Instance Methods

_define_after_model_callback(klass, callback) click to toggle source
# File lib/active_model/callbacks.rb, line 143
def _define_after_model_callback(klass, callback)
  klass.define_singleton_method("after_#{callback}") do |*args, **options, &block|
    options.assert_valid_keys(:if, :unless, :prepend)
    options[:prepend] = true
    conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
      v != false
    }
    options[:if] = Array(options[:if]) + [conditional]
    set_callback(:"#{callback}", :after, *args, options, &block)
  end
end
_define_around_model_callback(klass, callback) click to toggle source
# File lib/active_model/callbacks.rb, line 136
def _define_around_model_callback(klass, callback)
  klass.define_singleton_method("around_#{callback}") do |*args, **options, &block|
    options.assert_valid_keys(:if, :unless, :prepend)
    set_callback(:"#{callback}", :around, *args, options, &block)
  end
end
_define_before_model_callback(klass, callback) click to toggle source
# File lib/active_model/callbacks.rb, line 129
def _define_before_model_callback(klass, callback)
  klass.define_singleton_method("before_#{callback}") do |*args, **options, &block|
    options.assert_valid_keys(:if, :unless, :prepend)
    set_callback(:"#{callback}", :before, *args, options, &block)
  end
end