Skip to content
This repository
tree: 2a7cfee13e
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 139 lines (129 sloc) 4.594 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
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 callback 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 callbacks attached to:
  #
  # define_model_callbacks :create, :update
  #
  # 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:
  #
  # 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 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 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.
  module Callbacks
    def self.extended(base) #:nodoc:
      base.class_eval do
        include ActiveSupport::Callbacks
      end
    end

    # 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.
    #
    # define_model_callbacks :initializer, only: :after
    #
    # 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
    # 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
    def define_model_callbacks(*callbacks)
      options = callbacks.extract_options!
      options = {
        :terminator => "result == false",
        :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

    def _define_before_model_callback(klass, callback) #:nodoc:
      klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
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__ + 1
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__ + 1
def self.after_#{callback}(*args, &block)
options = args.extract_options!
options[:prepend] = true
options[:if] = Array(options[:if]) << "value != false"
set_callback(:#{callback}, :after, *(args << options), &block)
end
CALLBACK
    end
  end
end
Something went wrong with that request. Please try again.