2016-08-06 12:38:02 -04:00
|
|
|
require "active_support/core_ext/array/extract_options"
|
2013-01-31 09:44:04 -05:00
|
|
|
|
2009-12-28 19:15:39 -05:00
|
|
|
module ActiveModel
|
2012-10-21 02:26:01 -04:00
|
|
|
# == Active \Model \Callbacks
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# Provides an interface for any class to have Active Record like callbacks.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-06-15 14:18:10 -04:00
|
|
|
# Like the Active Record methods, the callback chain is aborted as soon as
|
2014-12-08 09:53:24 -05:00
|
|
|
# one of the methods throws +:abort+.
|
2010-01-14 16:19:53 -05:00
|
|
|
#
|
|
|
|
# First, extend ActiveModel::Callbacks from the class you are creating:
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# class MyModel
|
|
|
|
# extend ActiveModel::Callbacks
|
|
|
|
# end
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-06-15 14:18:10 -04:00
|
|
|
# Then define a list of methods that you want callbacks attached to:
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# define_model_callbacks :create, :update
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# This will provide all three standard callbacks (before, around and after)
|
|
|
|
# for both the <tt>:create</tt> and <tt>:update</tt> 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:
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# def create
|
2011-01-09 13:15:05 -05:00
|
|
|
# run_callbacks :create do
|
2010-01-14 16:19:53 -05:00
|
|
|
# # Your create action methods here
|
|
|
|
# end
|
|
|
|
# end
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# Then in your class, you can use the +before_create+, +after_create+ and
|
2013-12-21 11:44:43 -05:00
|
|
|
# +around_create+ methods, just as you would in an Active Record model.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# before_create :action_before_create
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# def action_before_create
|
|
|
|
# # Your code here
|
|
|
|
# end
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-08-07 18:16:28 -04:00
|
|
|
# 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
|
|
|
|
#
|
2015-01-31 01:29:02 -05:00
|
|
|
# You can choose to have only specific callbacks by passing a hash to the
|
2012-06-22 01:32:54 -04:00
|
|
|
# +define_model_callbacks+ method.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# define_model_callbacks :create, only: [:after, :before]
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# Would only create the +after_create+ and +before_create+ callback methods in
|
|
|
|
# your class.
|
2017-05-13 07:56:37 -04:00
|
|
|
#
|
|
|
|
# NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
|
|
|
|
#
|
2009-12-28 19:15:39 -05:00
|
|
|
module Callbacks
|
2012-06-22 17:29:59 -04:00
|
|
|
def self.extended(base) #:nodoc:
|
2009-12-28 19:15:39 -05:00
|
|
|
base.class_eval do
|
|
|
|
include ActiveSupport::Callbacks
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-06-22 01:32:54 -04:00
|
|
|
# define_model_callbacks accepts the same options +define_callbacks+ does,
|
|
|
|
# in case you want to overwrite a default. Besides that, it also accepts an
|
|
|
|
# <tt>:only</tt> option, where you can choose if you want all types (before,
|
|
|
|
# around or after) or just some.
|
2009-12-28 19:15:39 -05:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# define_model_callbacks :initializer, only: :after
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
|
|
|
|
# on that method call. To get around this you can call the define_model_callbacks
|
2010-01-14 16:19:53 -05:00
|
|
|
# method as many times as you need.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# define_model_callbacks :create, only: :after
|
|
|
|
# define_model_callbacks :update, only: :before
|
|
|
|
# define_model_callbacks :destroy, only: :around
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# Would create +after_create+, +before_update+ and +around_destroy+ methods
|
|
|
|
# only.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2012-06-22 01:32:54 -04:00
|
|
|
# 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.
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2009-12-28 19:15:39 -05:00
|
|
|
# class MyModel
|
2010-01-14 16:19:53 -05:00
|
|
|
# extend ActiveModel::Callbacks
|
2009-12-28 19:15:39 -05:00
|
|
|
# define_model_callbacks :create
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# before_create AnotherClass
|
2009-12-28 19:15:39 -05:00
|
|
|
# end
|
2010-08-14 01:13:00 -04:00
|
|
|
#
|
2010-01-14 16:19:53 -05:00
|
|
|
# class AnotherClass
|
|
|
|
# def self.before_create( obj )
|
2010-06-15 14:18:10 -04:00
|
|
|
# # obj is the MyModel instance that the callback is being called on
|
2009-12-28 19:15:39 -05:00
|
|
|
# end
|
|
|
|
# end
|
2014-10-03 13:34:01 -04:00
|
|
|
#
|
|
|
|
# NOTE: +method_name+ passed to `define_model_callbacks` must not end with
|
2014-10-03 17:31:14 -04:00
|
|
|
# `!`, `?` or `=`.
|
2009-12-28 19:15:39 -05:00
|
|
|
def define_model_callbacks(*callbacks)
|
|
|
|
options = callbacks.extract_options!
|
2010-08-14 01:13:00 -04:00
|
|
|
options = {
|
2013-05-01 20:10:06 -04:00
|
|
|
skip_after_callbacks_if_terminated: true,
|
|
|
|
scope: [:kind, :name],
|
|
|
|
only: [:before, :around, :after]
|
2012-06-25 12:12:49 -04:00
|
|
|
}.merge!(options)
|
2009-12-28 19:15:39 -05:00
|
|
|
|
2012-01-05 17:52:31 -05:00
|
|
|
types = Array(options.delete(:only))
|
2009-12-28 19:15:39 -05:00
|
|
|
|
|
|
|
callbacks.each do |callback|
|
|
|
|
define_callbacks(callback, options)
|
|
|
|
|
|
|
|
types.each do |type|
|
2010-12-27 03:30:36 -05:00
|
|
|
send("_define_#{type}_model_callback", self, callback)
|
2009-12-28 19:15:39 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-06-25 12:12:49 -04:00
|
|
|
private
|
|
|
|
|
2016-12-19 04:37:57 -05:00
|
|
|
def _define_before_model_callback(klass, callback)
|
2016-08-06 13:55:02 -04:00
|
|
|
klass.define_singleton_method("before_#{callback}") do |*args, &block|
|
|
|
|
set_callback(:"#{callback}", :before, *args, &block)
|
|
|
|
end
|
2013-05-08 13:14:12 -04:00
|
|
|
end
|
2009-12-28 19:15:39 -05:00
|
|
|
|
2016-12-19 04:37:57 -05:00
|
|
|
def _define_around_model_callback(klass, callback)
|
2016-08-06 13:55:02 -04:00
|
|
|
klass.define_singleton_method("around_#{callback}") do |*args, &block|
|
|
|
|
set_callback(:"#{callback}", :around, *args, &block)
|
|
|
|
end
|
2013-05-08 13:14:12 -04:00
|
|
|
end
|
2009-12-28 19:15:39 -05:00
|
|
|
|
2016-12-19 04:37:57 -05:00
|
|
|
def _define_after_model_callback(klass, callback)
|
2016-08-06 13:55:02 -04:00
|
|
|
klass.define_singleton_method("after_#{callback}") do |*args, &block|
|
|
|
|
options = args.extract_options!
|
|
|
|
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
|
2013-05-08 13:14:12 -04:00
|
|
|
end
|
2009-12-28 19:15:39 -05:00
|
|
|
end
|
2010-03-27 14:50:11 -04:00
|
|
|
end
|