3
class CallbackDeveloper < ActiveRecord::Base
4
set_table_name 'developers'
7
def callback_string(callback_method)
8
"history << [#{callback_method.to_sym.inspect}, :string]"
11
def callback_proc(callback_method)
12
Proc.new { |model| model.history << [callback_method, :proc] }
15
def define_callback_method(callback_method)
16
define_method("#{callback_method}_method") do |model|
17
model.history << [callback_method, :method]
21
def callback_object(callback_method)
23
klass.send(:define_method, callback_method) do |model|
24
model.history << [callback_method, :object]
30
ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
31
callback_method_sym = callback_method.to_sym
32
define_callback_method(callback_method_sym)
33
send(callback_method, callback_method_sym)
34
send(callback_method, callback_string(callback_method_sym))
35
send(callback_method, callback_proc(callback_method_sym))
36
send(callback_method, callback_object(callback_method_sym))
37
send(callback_method) { |model| model.history << [callback_method_sym, :block] }
44
# after_initialize and after_find are invoked only if instance methods have been defined.
52
class ParentDeveloper < ActiveRecord::Base
53
set_table_name 'developers'
54
attr_accessor :after_save_called
55
before_validation {|record| record.after_save_called = true}
58
class ChildDeveloper < ParentDeveloper
62
class RecursiveCallbackDeveloper < ActiveRecord::Base
63
set_table_name 'developers'
65
before_save :on_before_save
66
after_save :on_after_save
68
attr_reader :on_before_save_called, :on_after_save_called
71
@on_before_save_called ||= 0
72
@on_before_save_called += 1
73
save unless @on_before_save_called > 1
77
@on_after_save_called ||= 0
78
@on_after_save_called += 1
79
save unless @on_after_save_called > 1
83
class ImmutableDeveloper < ActiveRecord::Base
84
set_table_name 'developers'
86
validates_inclusion_of :salary, :in => 50000..200000
89
before_destroy :cancel
102
class ImmutableMethodDeveloper < ActiveRecord::Base
103
set_table_name 'developers'
105
validates_inclusion_of :salary, :in => 50000..200000
122
class CallbackCancellationDeveloper < ActiveRecord::Base
123
set_table_name 'developers'
125
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
126
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
128
def before_save; !@cancel_before_save; end
129
def before_create; !@cancel_before_create; end
130
def before_update; !@cancel_before_update; end
131
def before_destroy; !@cancel_before_destroy; end
133
def after_save; @after_save_called = true; end
134
def after_update; @after_update_called = true; end
135
def after_create; @after_create_called = true; end
136
def after_destroy; @after_destroy_called = true; end
139
class CallbacksTest < ActiveRecord::TestCase
143
david = CallbackDeveloper.new
145
[ :after_initialize, :string ],
146
[ :after_initialize, :proc ],
147
[ :after_initialize, :object ],
148
[ :after_initialize, :block ],
153
david = CallbackDeveloper.find(1)
155
[ :after_find, :string ],
156
[ :after_find, :proc ],
157
[ :after_find, :object ],
158
[ :after_find, :block ],
159
[ :after_initialize, :string ],
160
[ :after_initialize, :proc ],
161
[ :after_initialize, :object ],
162
[ :after_initialize, :block ],
167
david = CallbackDeveloper.new
170
[ :after_initialize, :string ],
171
[ :after_initialize, :proc ],
172
[ :after_initialize, :object ],
173
[ :after_initialize, :block ],
174
[ :before_validation, :string ],
175
[ :before_validation, :proc ],
176
[ :before_validation, :object ],
177
[ :before_validation, :block ],
178
[ :before_validation_on_create, :string ],
179
[ :before_validation_on_create, :proc ],
180
[ :before_validation_on_create, :object ],
181
[ :before_validation_on_create, :block ],
182
[ :after_validation, :string ],
183
[ :after_validation, :proc ],
184
[ :after_validation, :object ],
185
[ :after_validation, :block ],
186
[ :after_validation_on_create, :string ],
187
[ :after_validation_on_create, :proc ],
188
[ :after_validation_on_create, :object ],
189
[ :after_validation_on_create, :block ]
193
def test_existing_valid?
194
david = CallbackDeveloper.find(1)
197
[ :after_find, :string ],
198
[ :after_find, :proc ],
199
[ :after_find, :object ],
200
[ :after_find, :block ],
201
[ :after_initialize, :string ],
202
[ :after_initialize, :proc ],
203
[ :after_initialize, :object ],
204
[ :after_initialize, :block ],
205
[ :before_validation, :string ],
206
[ :before_validation, :proc ],
207
[ :before_validation, :object ],
208
[ :before_validation, :block ],
209
[ :before_validation_on_update, :string ],
210
[ :before_validation_on_update, :proc ],
211
[ :before_validation_on_update, :object ],
212
[ :before_validation_on_update, :block ],
213
[ :after_validation, :string ],
214
[ :after_validation, :proc ],
215
[ :after_validation, :object ],
216
[ :after_validation, :block ],
217
[ :after_validation_on_update, :string ],
218
[ :after_validation_on_update, :proc ],
219
[ :after_validation_on_update, :object ],
220
[ :after_validation_on_update, :block ]
225
david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
227
[ :after_initialize, :string ],
228
[ :after_initialize, :proc ],
229
[ :after_initialize, :object ],
230
[ :after_initialize, :block ],
231
[ :before_validation, :string ],
232
[ :before_validation, :proc ],
233
[ :before_validation, :object ],
234
[ :before_validation, :block ],
235
[ :before_validation_on_create, :string ],
236
[ :before_validation_on_create, :proc ],
237
[ :before_validation_on_create, :object ],
238
[ :before_validation_on_create, :block ],
239
[ :after_validation, :string ],
240
[ :after_validation, :proc ],
241
[ :after_validation, :object ],
242
[ :after_validation, :block ],
243
[ :after_validation_on_create, :string ],
244
[ :after_validation_on_create, :proc ],
245
[ :after_validation_on_create, :object ],
246
[ :after_validation_on_create, :block ],
247
[ :before_save, :string ],
248
[ :before_save, :proc ],
249
[ :before_save, :object ],
250
[ :before_save, :block ],
251
[ :before_create, :string ],
252
[ :before_create, :proc ],
253
[ :before_create, :object ],
254
[ :before_create, :block ],
255
[ :after_create, :string ],
256
[ :after_create, :proc ],
257
[ :after_create, :object ],
258
[ :after_create, :block ],
259
[ :after_save, :string ],
260
[ :after_save, :proc ],
261
[ :after_save, :object ],
262
[ :after_save, :block ]
267
david = CallbackDeveloper.find(1)
270
[ :after_find, :string ],
271
[ :after_find, :proc ],
272
[ :after_find, :object ],
273
[ :after_find, :block ],
274
[ :after_initialize, :string ],
275
[ :after_initialize, :proc ],
276
[ :after_initialize, :object ],
277
[ :after_initialize, :block ],
278
[ :before_validation, :string ],
279
[ :before_validation, :proc ],
280
[ :before_validation, :object ],
281
[ :before_validation, :block ],
282
[ :before_validation_on_update, :string ],
283
[ :before_validation_on_update, :proc ],
284
[ :before_validation_on_update, :object ],
285
[ :before_validation_on_update, :block ],
286
[ :after_validation, :string ],
287
[ :after_validation, :proc ],
288
[ :after_validation, :object ],
289
[ :after_validation, :block ],
290
[ :after_validation_on_update, :string ],
291
[ :after_validation_on_update, :proc ],
292
[ :after_validation_on_update, :object ],
293
[ :after_validation_on_update, :block ],
294
[ :before_save, :string ],
295
[ :before_save, :proc ],
296
[ :before_save, :object ],
297
[ :before_save, :block ],
298
[ :before_update, :string ],
299
[ :before_update, :proc ],
300
[ :before_update, :object ],
301
[ :before_update, :block ],
302
[ :after_update, :string ],
303
[ :after_update, :proc ],
304
[ :after_update, :object ],
305
[ :after_update, :block ],
306
[ :after_save, :string ],
307
[ :after_save, :proc ],
308
[ :after_save, :object ],
309
[ :after_save, :block ]
314
david = CallbackDeveloper.find(1)
317
[ :after_find, :string ],
318
[ :after_find, :proc ],
319
[ :after_find, :object ],
320
[ :after_find, :block ],
321
[ :after_initialize, :string ],
322
[ :after_initialize, :proc ],
323
[ :after_initialize, :object ],
324
[ :after_initialize, :block ],
325
[ :before_destroy, :string ],
326
[ :before_destroy, :proc ],
327
[ :before_destroy, :object ],
328
[ :before_destroy, :block ],
329
[ :after_destroy, :string ],
330
[ :after_destroy, :proc ],
331
[ :after_destroy, :object ],
332
[ :after_destroy, :block ]
337
david = CallbackDeveloper.find(1)
338
CallbackDeveloper.delete(david.id)
340
[ :after_find, :string ],
341
[ :after_find, :proc ],
342
[ :after_find, :object ],
343
[ :after_find, :block ],
344
[ :after_initialize, :string ],
345
[ :after_initialize, :proc ],
346
[ :after_initialize, :object ],
347
[ :after_initialize, :block ],
351
def test_before_save_returning_false
352
david = ImmutableDeveloper.find(1)
355
assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
357
david = ImmutableDeveloper.find(1)
358
david.salary = 10_000_000
361
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
363
someone = CallbackCancellationDeveloper.find(1)
364
someone.cancel_before_save = true
365
assert someone.valid?
367
assert_save_callbacks_not_called(someone)
370
def test_before_create_returning_false
371
someone = CallbackCancellationDeveloper.new
372
someone.cancel_before_create = true
373
assert someone.valid?
375
assert_save_callbacks_not_called(someone)
378
def test_before_update_returning_false
379
someone = CallbackCancellationDeveloper.find(1)
380
someone.cancel_before_update = true
381
assert someone.valid?
383
assert_save_callbacks_not_called(someone)
386
def test_before_destroy_returning_false
387
david = ImmutableDeveloper.find(1)
388
assert !david.destroy
389
assert_not_nil ImmutableDeveloper.find_by_id(1)
391
someone = CallbackCancellationDeveloper.find(1)
392
someone.cancel_before_destroy = true
393
assert !someone.destroy
394
assert !someone.after_destroy_called
397
def assert_save_callbacks_not_called(someone)
398
assert !someone.after_save_called
399
assert !someone.after_create_called
400
assert !someone.after_update_called
402
private :assert_save_callbacks_not_called
404
def test_zzz_callback_returning_false # must be run last since we modify CallbackDeveloper
405
david = CallbackDeveloper.find(1)
406
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :returning_false]; return false }
407
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
410
[ :after_find, :string ],
411
[ :after_find, :proc ],
412
[ :after_find, :object ],
413
[ :after_find, :block ],
414
[ :after_initialize, :string ],
415
[ :after_initialize, :proc ],
416
[ :after_initialize, :object ],
417
[ :after_initialize, :block ],
418
[ :before_validation, :string ],
419
[ :before_validation, :proc ],
420
[ :before_validation, :object ],
421
[ :before_validation, :block ],
422
[ :before_validation, :returning_false ]
426
def test_inheritence_of_callbacks
427
parent = ParentDeveloper.new
428
assert !parent.after_save_called
430
assert parent.after_save_called
432
child = ChildDeveloper.new
433
assert !child.after_save_called
435
assert child.after_save_called