callbacks.rb 10.2 KB
Newer Older
1 2
require 'active_support/core_ext/array/wrap'

D
Initial  
David Heinemeier Hansson 已提交
3
module ActiveRecord
4
  # = Active Record Callbacks
5
  #
6
  # Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
7
  # before or after an alteration of the object state. This can be used to make sure that associated and
P
Pratik Naik 已提交
8
  # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
9
  # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
P
Pratik Naik 已提交
10
  # the <tt>Base#save</tt> call for a new record:
11 12 13 14 15
  #
  # * (-) <tt>save</tt>
  # * (-) <tt>valid</tt>
  # * (1) <tt>before_validation</tt>
  # * (-) <tt>validate</tt>
16 17 18
  # * (2) <tt>after_validation</tt>
  # * (3) <tt>before_save</tt>
  # * (4) <tt>before_create</tt>
19
  # * (-) <tt>create</tt>
20 21
  # * (5) <tt>after_create</tt>
  # * (6) <tt>after_save</tt>
22
  # * (7) <tt>after_commit</tt>
23
  #
24 25 26 27 28
  # Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
  # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
  # <tt>after_rollback</tt>.
  #
  # That's a total of ten callbacks, which gives you immense power to react and prepare for each state in the
29
  # Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
30
  # except that each <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback.
D
Initial  
David Heinemeier Hansson 已提交
31 32 33
  #
  # Examples:
  #   class CreditCard < ActiveRecord::Base
34
  #     # Strip everything but digits, so the user can specify "555 234 34" or
D
Initial  
David Heinemeier Hansson 已提交
35
  #     # "5552-3434" or both will mean "55523434"
36
  #     before_validation(:on => :create) do
D
Initial  
David Heinemeier Hansson 已提交
37 38 39 40 41
  #       self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
  #     end
  #   end
  #
  #   class Subscription < ActiveRecord::Base
42 43 44 45 46 47
  #     before_create :record_signup
  #
  #     private
  #       def record_signup
  #         self.signed_up_on = Date.today
  #       end
D
Initial  
David Heinemeier Hansson 已提交
48 49 50 51
  #   end
  #
  #   class Firm < ActiveRecord::Base
  #     # Destroys the associated clients and people when the firm is destroyed
52 53 54
  #     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 已提交
55 56 57
  #
  # == Inheritable callback queues
  #
58 59
  # Besides the overwritable 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
60
  # queue that is kept intact down through an inheritance hierarchy.
D
Initial  
David Heinemeier Hansson 已提交
61 62 63 64 65 66 67 68 69
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy :destroy_author
  #   end
  #
  #   class Reply < Topic
  #     before_destroy :destroy_readers
  #   end
  #
70 71
  # 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 following situation
N
Neeraj Singh 已提交
72
  # where the +before_destroy+ methis is overriden:
D
Initial  
David Heinemeier Hansson 已提交
73 74 75 76 77 78 79 80 81
  #
  #   class Topic < ActiveRecord::Base
  #     def before_destroy() destroy_author end
  #   end
  #
  #   class Reply < Topic
  #     def before_destroy() destroy_readers end
  #   end
  #
82 83 84
  # 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 descendant
85
  # to decide whether they want to call +super+ and trigger the inherited callbacks.
D
Initial  
David Heinemeier Hansson 已提交
86
  #
87 88
  # *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
89
  # child before the parent has registered the callbacks and they won't be inherited.
90
  #
D
Initial  
David Heinemeier Hansson 已提交
91 92
  # == Types of callbacks
  #
93
  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
94 95
  # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
  # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
96
  # creating mix-ins), and inline eval methods are deprecated.
D
Initial  
David Heinemeier Hansson 已提交
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
  #
  # 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
P
Pratik Naik 已提交
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 139 140 141 142
  #     before_save      EncryptionWrapper.new
  #     after_save       EncryptionWrapper.new
  #     after_initialize EncryptionWrapper.new
  #   end
  #
  #   class EncryptionWrapper
  #     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
  #
  #     alias_method :after_find, :after_save
  #
  #     private
  #       def encrypt(value)
  #         # Secrecy is committed
  #       end
  #
  #       def decrypt(value)
  #         # Secrecy is unveiled
  #       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. You can make these callbacks more flexible by passing in other
  # initialization data such as the name of the attribute to work with:
  #
  #   class BankAccount < ActiveRecord::Base
D
Initial  
David Heinemeier Hansson 已提交
143 144 145 146 147 148 149 150 151 152 153
  #     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)
P
Pratik Naik 已提交
154
  #       record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
D
Initial  
David Heinemeier Hansson 已提交
155 156 157
  #     end
  #
  #     def after_save(record)
P
Pratik Naik 已提交
158
  #       record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
D
Initial  
David Heinemeier Hansson 已提交
159
  #     end
160
  #
D
David Heinemeier Hansson 已提交
161
  #     alias_method :after_find, :after_save
D
Initial  
David Heinemeier Hansson 已提交
162 163 164 165 166 167 168
  #
  #     private
  #       def encrypt(value)
  #         # Secrecy is committed
  #       end
  #
  #       def decrypt(value)
169
  #         # Secrecy is unveiled
D
Initial  
David Heinemeier Hansson 已提交
170 171 172
  #       end
  #   end
  #
173
  # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
174
  # pass a "method string", which will then be evaluated within the binding of the callback. Example:
D
Initial  
David Heinemeier Hansson 已提交
175 176 177 178 179
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy 'self.class.delete_all "parent_id = #{id}"'
  #   end
  #
180
  # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
181
  # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
D
Initial  
David Heinemeier Hansson 已提交
182 183
  #
  #   class Topic < ActiveRecord::Base
184
  #     before_destroy 'self.class.delete_all "parent_id = #{id}"',
D
Initial  
David Heinemeier Hansson 已提交
185 186 187
  #                    'puts "Evaluated after parents are destroyed"'
  #   end
  #
188
  # == The +after_find+ and +after_initialize+ exceptions
D
Initial  
David Heinemeier Hansson 已提交
189
  #
190 191 192
  # 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
193
  # run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
194
  # callback types will be called.
195
  #
196
  # == <tt>before_validation*</tt> returning statements
197
  #
198 199
  # 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+. If Base#save! is called it will raise a
200
  # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
201
  #
202
  # == Canceling callbacks
203
  #
204 205 206
  # 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 defined as
207
  # methods on the model, which are called last.
208 209 210 211 212 213 214 215 216 217 218 219
  #
  # == Transactions
  #
  # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
  # within a transaction. That includes <tt>after_*</tt> hooks. If everything
  # goes fine a COMMIT is executed once the chain has been completed.
  #
  # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
  # can also trigger a ROLLBACK raising an exception in any of the callbacks,
  # including <tt>after_*</tt> hooks. Note, however, that in that case the client
  # needs to be aware of it because an ordinary +save+ will raise such exception
  # instead of quietly returning +false+.
220
  #
D
Initial  
David Heinemeier Hansson 已提交
221
  module Callbacks
222
    extend ActiveSupport::Concern
223

224
    CALLBACKS = [
225
      :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
226 227
      :before_save, :around_save, :after_save, :before_create, :around_create,
      :after_create, :before_update, :around_update, :after_update,
228
      :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
229
    ]
D
Initial  
David Heinemeier Hansson 已提交
230

231
    included do
232
      extend ActiveModel::Callbacks
233
      include ActiveModel::Validations::Callbacks
234

235
      define_model_callbacks :initialize, :find, :touch, :only => :after
236
      define_model_callbacks :save, :create, :update, :destroy
D
Initial  
David Heinemeier Hansson 已提交
237 238
    end

239 240
    def destroy #:nodoc:
      _run_destroy_callbacks { super }
D
Initial  
David Heinemeier Hansson 已提交
241 242
    end

243 244 245 246
    def touch(*) #:nodoc:
      _run_touch_callbacks { super }
    end

247
  private
248

249 250
    def create_or_update #:nodoc:
      _run_save_callbacks { super }
251
    end
252

253 254
    def create #:nodoc:
      _run_create_callbacks { super }
255
    end
256

257 258
    def update(*) #:nodoc:
      _run_update_callbacks { super }
259
    end
D
Initial  
David Heinemeier Hansson 已提交
260
  end
261
end