callbacks.rb 12.3 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2 3
require 'observer'

module ActiveRecord
4
  # Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
5
  # before or after an alteration of the object state. This can be used to make sure that associated and
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  # dependent objects are deleted when destroy is called (by overwriting +before_destroy+) or to massage attributes
  # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
  # the <tt>Base#save</tt> call:
  #
  # * (-) <tt>save</tt>
  # * (-) <tt>valid</tt>
  # * (1) <tt>before_validation</tt>
  # * (2) <tt>before_validation_on_create</tt>
  # * (-) <tt>validate</tt>
  # * (-) <tt>validate_on_create</tt>
  # * (3) <tt>after_validation</tt>
  # * (4) <tt>after_validation_on_create</tt>
  # * (5) <tt>before_save</tt>
  # * (6) <tt>before_create</tt>
  # * (-) <tt>create</tt>
  # * (7) <tt>after_create</tt>
  # * (8) <tt>after_save</tt>
23
  #
24
  # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
25
  # Active Record lifecycle.
D
Initial  
David Heinemeier Hansson 已提交
26 27 28
  #
  # Examples:
  #   class CreditCard < ActiveRecord::Base
29
  #     # Strip everything but digits, so the user can specify "555 234 34" or
D
Initial  
David Heinemeier Hansson 已提交
30 31 32 33 34 35 36
  #     # "5552-3434" or both will mean "55523434"
  #     def before_validation_on_create
  #       self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
  #     end
  #   end
  #
  #   class Subscription < ActiveRecord::Base
37 38 39 40 41 42
  #     before_create :record_signup
  #
  #     private
  #       def record_signup
  #         self.signed_up_on = Date.today
  #       end
D
Initial  
David Heinemeier Hansson 已提交
43 44 45 46
  #   end
  #
  #   class Firm < ActiveRecord::Base
  #     # Destroys the associated clients and people when the firm is destroyed
47 48 49
  #     before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  #     before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
  #   end
D
Initial  
David Heinemeier Hansson 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  #
  # == Inheritable callback queues
  #
  # Besides the overwriteable callback methods, it's also possible to register callbacks through the use of the callback macros.
  # Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance
  # hierarchy. Example:
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy :destroy_author
  #   end
  #
  #   class Reply < Topic
  #     before_destroy :destroy_readers
  #   end
  #
65 66
  # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is run, both +destroy_author+ and
  # +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
D
Initial  
David Heinemeier Hansson 已提交
67 68 69 70 71 72 73 74 75 76
  # methods:
  #
  #   class Topic < ActiveRecord::Base
  #     def before_destroy() destroy_author end
  #   end
  #
  #   class Reply < Topic
  #     def before_destroy() destroy_readers end
  #   end
  #
77 78 79
  # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
  # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
  # when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
D
Initial  
David Heinemeier Hansson 已提交
80
  #
81 82 83 84
  # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
  # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
  # be inherited.
  #
D
Initial  
David Heinemeier Hansson 已提交
85 86
  # == Types of callbacks
  #
87
  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
D
Initial  
David Heinemeier Hansson 已提交
88
  # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
D
David Heinemeier Hansson 已提交
89
  # recommended approaches, inline methods using a proc are sometimes appropriate (such as for creating mix-ins), and inline
D
Initial  
David Heinemeier Hansson 已提交
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
  # eval methods are deprecated.
  #
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy :delete_parents
  #
  #     private
  #       def delete_parents
  #         self.class.delete_all "parent_id = #{id}"
  #       end
  #   end
  #
  # The callback objects have methods named after the callback called with the record as the only parameter, such as:
  #
  #   class BankAccount < ActiveRecord::Base
  #     before_save      EncryptionWrapper.new("credit_card_number")
  #     after_save       EncryptionWrapper.new("credit_card_number")
  #     after_initialize EncryptionWrapper.new("credit_card_number")
  #   end
  #
  #   class EncryptionWrapper
  #     def initialize(attribute)
  #       @attribute = attribute
  #     end
  #
  #     def before_save(record)
  #       record.credit_card_number = encrypt(record.credit_card_number)
  #     end
  #
  #     def after_save(record)
  #       record.credit_card_number = decrypt(record.credit_card_number)
  #     end
123
  #
D
David Heinemeier Hansson 已提交
124
  #     alias_method :after_find, :after_save
D
Initial  
David Heinemeier Hansson 已提交
125 126 127 128 129 130 131
  #
  #     private
  #       def encrypt(value)
  #         # Secrecy is committed
  #       end
  #
  #       def decrypt(value)
132
  #         # Secrecy is unveiled
D
Initial  
David Heinemeier Hansson 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145
  #       end
  #   end
  #
  # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
  # a method by the name of the callback messaged.
  #
  # The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string",
  # which will then be evaluated within the binding of the callback. Example:
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy 'self.class.delete_all "parent_id = #{id}"'
  #   end
  #
146
  # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback is triggered. Also note that these
D
Initial  
David Heinemeier Hansson 已提交
147 148 149
  # inline callbacks can be stacked just like the regular ones:
  #
  #   class Topic < ActiveRecord::Base
150
  #     before_destroy 'self.class.delete_all "parent_id = #{id}"',
D
Initial  
David Heinemeier Hansson 已提交
151 152 153
  #                    'puts "Evaluated after parents are destroyed"'
  #   end
  #
154
  # == The +after_find+ and +after_initialize+ exceptions
D
Initial  
David Heinemeier Hansson 已提交
155
  #
156 157 158
  # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as <tt>Base.find(:all)</tt>, we've had
  # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
  # +after_initialize+ will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
159
  # callback types will be called.
160
  #
161
  # == <tt>before_validation*</tt> returning statements
162
  #
163
  # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and <tt>Base#save</tt> will return +false+.
P
Pratik Naik 已提交
164
  # If Base#save! is called it will raise a RecordNotSaved exception.
165 166
  # Nothing will be appended to the errors object.
  #
167
  # == Canceling callbacks
168
  #
169 170
  # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are cancelled. If an <tt>after_*</tt> callback returns
  # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
171
  # defined as methods on the model, which are called last.
D
Initial  
David Heinemeier Hansson 已提交
172
  module Callbacks
173 174
    CALLBACKS = %w(
      after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
D
Initial  
David Heinemeier Hansson 已提交
175 176 177 178
      after_validation before_validation_on_create after_validation_on_create before_validation_on_update
      after_validation_on_update before_destroy after_destroy
    )

179
    def self.included(base) #:nodoc:
180
      base.extend Observable
D
Initial  
David Heinemeier Hansson 已提交
181

182 183
      [:create_or_update, :valid?, :create, :update, :destroy].each do |method|
        base.send :alias_method_chain, method, :callbacks
D
Initial  
David Heinemeier Hansson 已提交
184 185
      end

186 187
      base.send :include, ActiveSupport::Callbacks
      base.define_callbacks *CALLBACKS
D
Initial  
David Heinemeier Hansson 已提交
188 189
    end

190
    # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
191
    #def after_find() end
D
Initial  
David Heinemeier Hansson 已提交
192

193
    # Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
194 195
    #def after_initialize() end

196
    # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
D
Initial  
David Heinemeier Hansson 已提交
197 198
    def before_save() end

199
    # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
200 201 202 203
    #
    #  class Contact < ActiveRecord::Base
    #    after_save { logger.info( 'New contact saved!' ) }
    #  end
D
Initial  
David Heinemeier Hansson 已提交
204 205
    def after_save()  end
    def create_or_update_with_callbacks #:nodoc:
206
      return false if callback(:before_save) == false
207
      result = create_or_update_without_callbacks
D
Initial  
David Heinemeier Hansson 已提交
208
      callback(:after_save)
209
      result
D
Initial  
David Heinemeier Hansson 已提交
210
    end
211
    private :create_or_update_with_callbacks
D
Initial  
David Heinemeier Hansson 已提交
212

213
    # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
D
Initial  
David Heinemeier Hansson 已提交
214 215
    def before_create() end

216
    # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
D
Initial  
David Heinemeier Hansson 已提交
217 218
    def after_create() end
    def create_with_callbacks #:nodoc:
219
      return false if callback(:before_create) == false
220
      result = create_without_callbacks
D
Initial  
David Heinemeier Hansson 已提交
221
      callback(:after_create)
222
      result
D
Initial  
David Heinemeier Hansson 已提交
223
    end
224
    private :create_with_callbacks
D
Initial  
David Heinemeier Hansson 已提交
225

226
    # Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
D
Initial  
David Heinemeier Hansson 已提交
227 228
    def before_update() end

229
    # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
D
Initial  
David Heinemeier Hansson 已提交
230 231
    def after_update() end

232
    def update_with_callbacks(*args) #:nodoc:
233
      return false if callback(:before_update) == false
234
      result = update_without_callbacks(*args)
D
Initial  
David Heinemeier Hansson 已提交
235
      callback(:after_update)
236
      result
D
Initial  
David Heinemeier Hansson 已提交
237
    end
238
    private :update_with_callbacks
D
Initial  
David Heinemeier Hansson 已提交
239

240
    # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
D
Initial  
David Heinemeier Hansson 已提交
241 242
    def before_validation() end

243
    # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
D
Initial  
David Heinemeier Hansson 已提交
244 245
    def after_validation() end

246
    # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
D
Initial  
David Heinemeier Hansson 已提交
247 248 249
    # that haven't been saved yet (no record exists).
    def before_validation_on_create() end

250
    # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
D
Initial  
David Heinemeier Hansson 已提交
251 252 253
    # that haven't been saved yet (no record exists).
    def after_validation_on_create()  end

254
    # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
D
David Heinemeier Hansson 已提交
255
    # existing objects that have a record.
D
Initial  
David Heinemeier Hansson 已提交
256 257
    def before_validation_on_update() end

258
    # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
D
David Heinemeier Hansson 已提交
259
    # existing objects that have a record.
D
Initial  
David Heinemeier Hansson 已提交
260 261
    def after_validation_on_update()  end

262
    def valid_with_callbacks? #:nodoc:
263
      return false if callback(:before_validation) == false
264
      if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
265
      return false if result == false
D
Initial  
David Heinemeier Hansson 已提交
266

J
Jeremy Kemper 已提交
267
      result = valid_without_callbacks?
D
Initial  
David Heinemeier Hansson 已提交
268 269

      callback(:after_validation)
270
      if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
271

D
Initial  
David Heinemeier Hansson 已提交
272 273 274
      return result
    end

275
    # Is called _before_ <tt>Base.destroy</tt>.
276 277
    #
    # Note: If you need to _destroy_ or _nullify_ associated records first,
278
    # use the <tt>:dependent</tt> option on your associations.
D
Initial  
David Heinemeier Hansson 已提交
279 280
    def before_destroy() end

281
    # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
282 283 284 285
    #
    #  class Contact < ActiveRecord::Base
    #    after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
    #  end
D
Initial  
David Heinemeier Hansson 已提交
286 287
    def after_destroy()  end
    def destroy_with_callbacks #:nodoc:
288
      return false if callback(:before_destroy) == false
289
      result = destroy_without_callbacks
D
Initial  
David Heinemeier Hansson 已提交
290
      callback(:after_destroy)
291
      result
D
Initial  
David Heinemeier Hansson 已提交
292 293
    end

294 295
    private
      def callback(method)
296 297
        notify(method)

298
        result = run_callbacks(method) { |result, object| result == false }
299

300 301 302
        if result != false && respond_to_without_attributes?(method)
          result = send(method)
        end
303

304
        return result
D
Initial  
David Heinemeier Hansson 已提交
305
      end
306

307 308 309 310
      def notify(method) #:nodoc:
        self.class.changed
        self.class.notify_observers(method, self)
      end
D
Initial  
David Heinemeier Hansson 已提交
311
  end
312
end