callbacks.rb 9.9 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
  # 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>.
  #
28 29 30 31 32
  # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that 
  # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
  # are instantiated as well.
  #
  # That's a total of twelve callbacks, which gives you immense power to react and prepare for each state in the
33
  # Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
34
  # except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
D
Initial  
David Heinemeier Hansson 已提交
35 36 37
  #
  # Examples:
  #   class CreditCard < ActiveRecord::Base
38
  #     # Strip everything but digits, so the user can specify "555 234 34" or
D
Initial  
David Heinemeier Hansson 已提交
39
  #     # "5552-3434" or both will mean "55523434"
40
  #     before_validation(:on => :create) do
D
Initial  
David Heinemeier Hansson 已提交
41 42 43 44 45
  #       self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
  #     end
  #   end
  #
  #   class Subscription < ActiveRecord::Base
46 47 48 49 50 51
  #     before_create :record_signup
  #
  #     private
  #       def record_signup
  #         self.signed_up_on = Date.today
  #       end
D
Initial  
David Heinemeier Hansson 已提交
52 53 54 55
  #   end
  #
  #   class Firm < ActiveRecord::Base
  #     # Destroys the associated clients and people when the firm is destroyed
56 57 58
  #     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 已提交
59 60 61
  #
  # == Inheritable callback queues
  #
62 63
  # 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
64
  # queue that is kept intact down through an inheritance hierarchy.
D
Initial  
David Heinemeier Hansson 已提交
65 66 67 68 69 70 71 72 73
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy :destroy_author
  #   end
  #
  #   class Reply < Topic
  #     before_destroy :destroy_readers
  #   end
  #
74 75
  # 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 已提交
76
  # where the +before_destroy+ methis is overriden:
D
Initial  
David Heinemeier Hansson 已提交
77 78 79 80 81 82 83 84 85
  #
  #   class Topic < ActiveRecord::Base
  #     def before_destroy() destroy_author end
  #   end
  #
  #   class Reply < Topic
  #     def before_destroy() destroy_readers end
  #   end
  #
86 87 88
  # 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
89
  # to decide whether they want to call +super+ and trigger the inherited callbacks.
D
Initial  
David Heinemeier Hansson 已提交
90
  #
91 92
  # *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
93
  # child before the parent has registered the callbacks and they won't be inherited.
94
  #
D
Initial  
David Heinemeier Hansson 已提交
95 96
  # == Types of callbacks
  #
97
  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
98 99
  # 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
100
  # creating mix-ins), and inline eval methods are deprecated.
D
Initial  
David Heinemeier Hansson 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
  #
  # 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 已提交
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 143 144 145 146
  #     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 已提交
147 148 149 150 151 152 153 154 155 156 157
  #     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 已提交
158
  #       record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
D
Initial  
David Heinemeier Hansson 已提交
159 160 161
  #     end
  #
  #     def after_save(record)
P
Pratik Naik 已提交
162
  #       record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
D
Initial  
David Heinemeier Hansson 已提交
163
  #     end
164
  #
D
David Heinemeier Hansson 已提交
165
  #     alias_method :after_find, :after_save
D
Initial  
David Heinemeier Hansson 已提交
166 167 168 169 170 171 172
  #
  #     private
  #       def encrypt(value)
  #         # Secrecy is committed
  #       end
  #
  #       def decrypt(value)
173
  #         # Secrecy is unveiled
D
Initial  
David Heinemeier Hansson 已提交
174 175 176
  #       end
  #   end
  #
177
  # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
178
  # pass a "method string", which will then be evaluated within the binding of the callback. Example:
D
Initial  
David Heinemeier Hansson 已提交
179 180 181 182 183
  #
  #   class Topic < ActiveRecord::Base
  #     before_destroy 'self.class.delete_all "parent_id = #{id}"'
  #   end
  #
184
  # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
185
  # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
D
Initial  
David Heinemeier Hansson 已提交
186 187
  #
  #   class Topic < ActiveRecord::Base
188
  #     before_destroy 'self.class.delete_all "parent_id = #{id}"',
D
Initial  
David Heinemeier Hansson 已提交
189 190 191
  #                    'puts "Evaluated after parents are destroyed"'
  #   end
  #
192
  # == <tt>before_validation*</tt> returning statements
193
  #
194 195
  # 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
196
  # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
197
  #
198
  # == Canceling callbacks
199
  #
200 201 202
  # 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
203
  # methods on the model, which are called last.
204 205 206 207 208 209 210 211 212 213 214 215
  #
  # == 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+.
216
  #
D
Initial  
David Heinemeier Hansson 已提交
217
  module Callbacks
218
    extend ActiveSupport::Concern
219

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

227
    included do
228
      extend ActiveModel::Callbacks
229
      include ActiveModel::Validations::Callbacks
230

231
      define_model_callbacks :initialize, :find, :touch, :only => :after
232
      define_model_callbacks :save, :create, :update, :destroy
D
Initial  
David Heinemeier Hansson 已提交
233 234
    end

235
    def destroy #:nodoc:
236
      run_callbacks(:destroy) { super }
D
Initial  
David Heinemeier Hansson 已提交
237 238
    end

239
    def touch(*) #:nodoc:
240
      run_callbacks(:touch) { super }
241 242
    end

243
  private
244

245
    def create_or_update #:nodoc:
246
      run_callbacks(:save) { super }
247
    end
248

249
    def create #:nodoc:
250
      run_callbacks(:create) { super }
251
    end
252

253
    def update(*) #:nodoc:
254
      run_callbacks(:update) { super }
255
    end
D
Initial  
David Heinemeier Hansson 已提交
256
  end
257
end