require 'active_support/callbacks' module ActiveModel # == Active Model Callbacks # # Provides an interface for any class to have Active Record like callbacks. # # Like the Active Record methods, the call back chain is aborted as soon as # one of the methods in the chain returns false. # # 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 call backs attached to: # # define_model_callbacks :create, :update # # This will provide all three standard callbacks (before, around and after) around # both the :create and :update methods. To implement, you need to wrap the methods # you want call backs on in a block so that the call backs get a chance to fire: # # def create # _run_create_callbacks do # # Your create action methods here # end # end # # The _run__callbacks methods are dynamically created when you extend # the ActiveModel::Callbacks module. # # Then in your class, you can use the +before_create+, +after_create+ and +around_create+ # methods, just as you would in an Active Record module. # # before_create :action_before_create # # def action_before_create # # Your code here # end # # You can choose not to have all three callbacks by passing an 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. module Callbacks def self.extended(base) base.class_eval do include ActiveSupport::Callbacks end end # define_model_callbacks accepts all 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 => 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_, after_ and around_, in which # case the call back will call that class's _ 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 # def define_model_callbacks(*callbacks) options = callbacks.extract_options! options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options) types = Array(options.delete(:only)) types = [:before, :around, :after] if types.empty? callbacks.each do |callback| define_callbacks(callback, options) types.each do |type| send(:"_define_#{type}_model_callback", self, callback) end end end def _define_before_model_callback(klass, callback) #:nodoc: klass.class_eval <<-CALLBACK, __FILE__, __LINE__ def self.before_#{callback}(*args, &block) set_callback(:#{callback}, :before, *args, &block) end CALLBACK end def _define_around_model_callback(klass, callback) #:nodoc: klass.class_eval <<-CALLBACK, __FILE__, __LINE__ def self.around_#{callback}(*args, &block) set_callback(:#{callback}, :around, *args, &block) end CALLBACK end def _define_after_model_callback(klass, callback) #:nodoc: klass.class_eval <<-CALLBACK, __FILE__, __LINE__ def self.after_#{callback}(*args, &block) options = args.extract_options! options[:prepend] = true options[:if] = Array(options[:if]) << "!halted && value != false" set_callback(:#{callback}, :after, *(args << options), &block) end CALLBACK end end end