2008-01-21 12:20:51 -05:00
|
|
|
require "cases/helper"
|
2005-01-01 11:14:15 -05:00
|
|
|
|
|
|
|
class CallbackDeveloper < ActiveRecord::Base
|
2005-06-13 06:52:53 -04:00
|
|
|
set_table_name 'developers'
|
2005-01-01 11:14:15 -05:00
|
|
|
|
2005-06-13 06:52:53 -04:00
|
|
|
class << self
|
2005-01-01 11:14:15 -05:00
|
|
|
def callback_string(callback_method)
|
|
|
|
"history << [#{callback_method.to_sym.inspect}, :string]"
|
|
|
|
end
|
|
|
|
|
|
|
|
def callback_proc(callback_method)
|
|
|
|
Proc.new { |model| model.history << [callback_method, :proc] }
|
|
|
|
end
|
|
|
|
|
|
|
|
def define_callback_method(callback_method)
|
|
|
|
define_method("#{callback_method}_method") do |model|
|
|
|
|
model.history << [callback_method, :method]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def callback_object(callback_method)
|
|
|
|
klass = Class.new
|
|
|
|
klass.send(:define_method, callback_method) do |model|
|
|
|
|
model.history << [callback_method, :object]
|
|
|
|
end
|
|
|
|
klass.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
|
|
|
|
callback_method_sym = callback_method.to_sym
|
|
|
|
define_callback_method(callback_method_sym)
|
|
|
|
send(callback_method, callback_method_sym)
|
|
|
|
send(callback_method, callback_string(callback_method_sym))
|
|
|
|
send(callback_method, callback_proc(callback_method_sym))
|
|
|
|
send(callback_method, callback_object(callback_method_sym))
|
|
|
|
send(callback_method) { |model| model.history << [callback_method_sym, :block] }
|
|
|
|
end
|
|
|
|
|
|
|
|
def history
|
|
|
|
@history ||= []
|
|
|
|
end
|
|
|
|
|
2005-03-13 19:04:14 -05:00
|
|
|
# after_initialize and after_find are invoked only if instance methods have been defined.
|
2005-01-01 11:14:15 -05:00
|
|
|
def after_initialize
|
|
|
|
end
|
|
|
|
|
|
|
|
def after_find
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2007-01-23 22:04:48 -05:00
|
|
|
class ParentDeveloper < ActiveRecord::Base
|
|
|
|
set_table_name 'developers'
|
|
|
|
attr_accessor :after_save_called
|
|
|
|
before_validation {|record| record.after_save_called = true}
|
|
|
|
end
|
|
|
|
|
|
|
|
class ChildDeveloper < ParentDeveloper
|
2008-01-18 02:30:42 -05:00
|
|
|
|
2007-01-23 22:04:48 -05:00
|
|
|
end
|
|
|
|
|
2005-06-13 06:52:53 -04:00
|
|
|
class RecursiveCallbackDeveloper < ActiveRecord::Base
|
|
|
|
set_table_name 'developers'
|
|
|
|
|
|
|
|
before_save :on_before_save
|
|
|
|
after_save :on_after_save
|
|
|
|
|
|
|
|
attr_reader :on_before_save_called, :on_after_save_called
|
|
|
|
|
|
|
|
def on_before_save
|
|
|
|
@on_before_save_called ||= 0
|
|
|
|
@on_before_save_called += 1
|
|
|
|
save unless @on_before_save_called > 1
|
|
|
|
end
|
|
|
|
|
|
|
|
def on_after_save
|
|
|
|
@on_after_save_called ||= 0
|
|
|
|
@on_after_save_called += 1
|
|
|
|
save unless @on_after_save_called > 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2005-09-27 20:09:50 -04:00
|
|
|
class ImmutableDeveloper < ActiveRecord::Base
|
|
|
|
set_table_name 'developers'
|
|
|
|
|
2005-10-01 22:00:50 -04:00
|
|
|
validates_inclusion_of :salary, :in => 50000..200000
|
|
|
|
|
|
|
|
before_save :cancel
|
|
|
|
before_destroy :cancel
|
|
|
|
|
|
|
|
def cancelled?
|
|
|
|
@cancelled == true
|
2005-09-27 20:09:50 -04:00
|
|
|
end
|
2005-10-01 22:00:50 -04:00
|
|
|
|
|
|
|
private
|
|
|
|
def cancel
|
|
|
|
@cancelled = true
|
|
|
|
false
|
|
|
|
end
|
2005-09-27 20:09:50 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
class ImmutableMethodDeveloper < ActiveRecord::Base
|
|
|
|
set_table_name 'developers'
|
|
|
|
|
2005-10-01 22:00:50 -04:00
|
|
|
validates_inclusion_of :salary, :in => 50000..200000
|
|
|
|
|
|
|
|
def cancelled?
|
|
|
|
@cancelled == true
|
|
|
|
end
|
|
|
|
|
|
|
|
def before_save
|
|
|
|
@cancelled = true
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
def before_destroy
|
|
|
|
@cancelled = true
|
|
|
|
false
|
2005-09-27 20:09:50 -04:00
|
|
|
end
|
|
|
|
end
|
2005-01-01 11:14:15 -05:00
|
|
|
|
2006-10-04 11:38:59 -04:00
|
|
|
class CallbackCancellationDeveloper < ActiveRecord::Base
|
|
|
|
set_table_name 'developers'
|
2009-01-09 18:09:50 -05:00
|
|
|
|
|
|
|
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
|
|
|
|
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
|
|
|
|
|
|
|
|
def before_save; !@cancel_before_save; end
|
|
|
|
def before_create; !@cancel_before_create; end
|
|
|
|
def before_update; !@cancel_before_update; end
|
|
|
|
def before_destroy; !@cancel_before_destroy; end
|
|
|
|
|
|
|
|
def after_save; @after_save_called = true; end
|
|
|
|
def after_update; @after_update_called = true; end
|
|
|
|
def after_create; @after_create_called = true; end
|
|
|
|
def after_destroy; @after_destroy_called = true; end
|
2006-10-04 11:38:59 -04:00
|
|
|
end
|
|
|
|
|
2008-01-21 12:20:51 -05:00
|
|
|
class CallbacksTest < ActiveRecord::TestCase
|
2005-06-10 10:58:02 -04:00
|
|
|
fixtures :developers
|
2005-01-01 11:14:15 -05:00
|
|
|
|
|
|
|
def test_initialize
|
|
|
|
david = CallbackDeveloper.new
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_find
|
|
|
|
david = CallbackDeveloper.find(1)
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_find, :string ],
|
|
|
|
[ :after_find, :proc ],
|
|
|
|
[ :after_find, :object ],
|
|
|
|
[ :after_find, :block ],
|
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_new_valid?
|
|
|
|
david = CallbackDeveloper.new
|
|
|
|
david.valid?
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
[ :before_validation, :string ],
|
|
|
|
[ :before_validation, :proc ],
|
|
|
|
[ :before_validation, :object ],
|
|
|
|
[ :before_validation, :block ],
|
|
|
|
[ :before_validation_on_create, :string ],
|
|
|
|
[ :before_validation_on_create, :proc ],
|
|
|
|
[ :before_validation_on_create, :object ],
|
|
|
|
[ :before_validation_on_create, :block ],
|
|
|
|
[ :after_validation, :string ],
|
|
|
|
[ :after_validation, :proc ],
|
|
|
|
[ :after_validation, :object ],
|
|
|
|
[ :after_validation, :block ],
|
|
|
|
[ :after_validation_on_create, :string ],
|
|
|
|
[ :after_validation_on_create, :proc ],
|
|
|
|
[ :after_validation_on_create, :object ],
|
|
|
|
[ :after_validation_on_create, :block ]
|
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_existing_valid?
|
|
|
|
david = CallbackDeveloper.find(1)
|
|
|
|
david.valid?
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_find, :string ],
|
|
|
|
[ :after_find, :proc ],
|
|
|
|
[ :after_find, :object ],
|
|
|
|
[ :after_find, :block ],
|
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
[ :before_validation, :string ],
|
|
|
|
[ :before_validation, :proc ],
|
|
|
|
[ :before_validation, :object ],
|
|
|
|
[ :before_validation, :block ],
|
|
|
|
[ :before_validation_on_update, :string ],
|
|
|
|
[ :before_validation_on_update, :proc ],
|
|
|
|
[ :before_validation_on_update, :object ],
|
|
|
|
[ :before_validation_on_update, :block ],
|
|
|
|
[ :after_validation, :string ],
|
|
|
|
[ :after_validation, :proc ],
|
|
|
|
[ :after_validation, :object ],
|
|
|
|
[ :after_validation, :block ],
|
|
|
|
[ :after_validation_on_update, :string ],
|
|
|
|
[ :after_validation_on_update, :proc ],
|
|
|
|
[ :after_validation_on_update, :object ],
|
|
|
|
[ :after_validation_on_update, :block ]
|
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_create
|
|
|
|
david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
[ :before_validation, :string ],
|
|
|
|
[ :before_validation, :proc ],
|
|
|
|
[ :before_validation, :object ],
|
|
|
|
[ :before_validation, :block ],
|
|
|
|
[ :before_validation_on_create, :string ],
|
|
|
|
[ :before_validation_on_create, :proc ],
|
|
|
|
[ :before_validation_on_create, :object ],
|
|
|
|
[ :before_validation_on_create, :block ],
|
|
|
|
[ :after_validation, :string ],
|
|
|
|
[ :after_validation, :proc ],
|
|
|
|
[ :after_validation, :object ],
|
|
|
|
[ :after_validation, :block ],
|
|
|
|
[ :after_validation_on_create, :string ],
|
|
|
|
[ :after_validation_on_create, :proc ],
|
|
|
|
[ :after_validation_on_create, :object ],
|
|
|
|
[ :after_validation_on_create, :block ],
|
|
|
|
[ :before_save, :string ],
|
|
|
|
[ :before_save, :proc ],
|
|
|
|
[ :before_save, :object ],
|
|
|
|
[ :before_save, :block ],
|
|
|
|
[ :before_create, :string ],
|
|
|
|
[ :before_create, :proc ],
|
|
|
|
[ :before_create, :object ],
|
|
|
|
[ :before_create, :block ],
|
|
|
|
[ :after_create, :string ],
|
|
|
|
[ :after_create, :proc ],
|
|
|
|
[ :after_create, :object ],
|
|
|
|
[ :after_create, :block ],
|
|
|
|
[ :after_save, :string ],
|
|
|
|
[ :after_save, :proc ],
|
|
|
|
[ :after_save, :object ],
|
|
|
|
[ :after_save, :block ]
|
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_save
|
|
|
|
david = CallbackDeveloper.find(1)
|
|
|
|
david.save
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_find, :string ],
|
|
|
|
[ :after_find, :proc ],
|
|
|
|
[ :after_find, :object ],
|
|
|
|
[ :after_find, :block ],
|
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
[ :before_validation, :string ],
|
|
|
|
[ :before_validation, :proc ],
|
|
|
|
[ :before_validation, :object ],
|
|
|
|
[ :before_validation, :block ],
|
|
|
|
[ :before_validation_on_update, :string ],
|
|
|
|
[ :before_validation_on_update, :proc ],
|
|
|
|
[ :before_validation_on_update, :object ],
|
|
|
|
[ :before_validation_on_update, :block ],
|
|
|
|
[ :after_validation, :string ],
|
|
|
|
[ :after_validation, :proc ],
|
|
|
|
[ :after_validation, :object ],
|
|
|
|
[ :after_validation, :block ],
|
|
|
|
[ :after_validation_on_update, :string ],
|
|
|
|
[ :after_validation_on_update, :proc ],
|
|
|
|
[ :after_validation_on_update, :object ],
|
|
|
|
[ :after_validation_on_update, :block ],
|
|
|
|
[ :before_save, :string ],
|
|
|
|
[ :before_save, :proc ],
|
|
|
|
[ :before_save, :object ],
|
|
|
|
[ :before_save, :block ],
|
|
|
|
[ :before_update, :string ],
|
|
|
|
[ :before_update, :proc ],
|
|
|
|
[ :before_update, :object ],
|
|
|
|
[ :before_update, :block ],
|
|
|
|
[ :after_update, :string ],
|
|
|
|
[ :after_update, :proc ],
|
|
|
|
[ :after_update, :object ],
|
|
|
|
[ :after_update, :block ],
|
|
|
|
[ :after_save, :string ],
|
|
|
|
[ :after_save, :proc ],
|
|
|
|
[ :after_save, :object ],
|
|
|
|
[ :after_save, :block ]
|
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_destroy
|
|
|
|
david = CallbackDeveloper.find(1)
|
|
|
|
david.destroy
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_find, :string ],
|
|
|
|
[ :after_find, :proc ],
|
|
|
|
[ :after_find, :object ],
|
|
|
|
[ :after_find, :block ],
|
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
[ :before_destroy, :string ],
|
|
|
|
[ :before_destroy, :proc ],
|
|
|
|
[ :before_destroy, :object ],
|
|
|
|
[ :before_destroy, :block ],
|
|
|
|
[ :after_destroy, :string ],
|
|
|
|
[ :after_destroy, :proc ],
|
|
|
|
[ :after_destroy, :object ],
|
|
|
|
[ :after_destroy, :block ]
|
|
|
|
], david.history
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_delete
|
|
|
|
david = CallbackDeveloper.find(1)
|
|
|
|
CallbackDeveloper.delete(david.id)
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_find, :string ],
|
|
|
|
[ :after_find, :proc ],
|
|
|
|
[ :after_find, :object ],
|
|
|
|
[ :after_find, :block ],
|
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-01 11:14:15 -05:00
|
|
|
], david.history
|
|
|
|
end
|
2005-10-01 22:00:50 -04:00
|
|
|
|
|
|
|
def test_before_save_returning_false
|
|
|
|
david = ImmutableDeveloper.find(1)
|
|
|
|
assert david.valid?
|
|
|
|
assert !david.save
|
2006-02-28 15:37:21 -05:00
|
|
|
assert_raises(ActiveRecord::RecordNotSaved) { david.save! }
|
2005-10-01 22:00:50 -04:00
|
|
|
|
2006-02-28 15:37:21 -05:00
|
|
|
david = ImmutableDeveloper.find(1)
|
2005-10-01 22:00:50 -04:00
|
|
|
david.salary = 10_000_000
|
|
|
|
assert !david.valid?
|
|
|
|
assert !david.save
|
2006-02-28 15:37:21 -05:00
|
|
|
assert_raises(ActiveRecord::RecordInvalid) { david.save! }
|
2009-01-09 18:09:50 -05:00
|
|
|
|
|
|
|
someone = CallbackCancellationDeveloper.find(1)
|
|
|
|
someone.cancel_before_save = true
|
|
|
|
assert someone.valid?
|
|
|
|
assert !someone.save
|
|
|
|
assert_save_callbacks_not_called(someone)
|
2005-10-01 22:00:50 -04:00
|
|
|
end
|
|
|
|
|
2006-10-04 11:38:59 -04:00
|
|
|
def test_before_create_returning_false
|
|
|
|
someone = CallbackCancellationDeveloper.new
|
2009-01-09 18:09:50 -05:00
|
|
|
someone.cancel_before_create = true
|
2006-10-04 11:38:59 -04:00
|
|
|
assert someone.valid?
|
|
|
|
assert !someone.save
|
2009-01-09 18:09:50 -05:00
|
|
|
assert_save_callbacks_not_called(someone)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_before_update_returning_false
|
|
|
|
someone = CallbackCancellationDeveloper.find(1)
|
|
|
|
someone.cancel_before_update = true
|
|
|
|
assert someone.valid?
|
|
|
|
assert !someone.save
|
|
|
|
assert_save_callbacks_not_called(someone)
|
2006-10-04 11:38:59 -04:00
|
|
|
end
|
|
|
|
|
2005-09-27 20:09:50 -04:00
|
|
|
def test_before_destroy_returning_false
|
|
|
|
david = ImmutableDeveloper.find(1)
|
2006-02-28 15:37:21 -05:00
|
|
|
assert !david.destroy
|
2005-10-01 22:00:50 -04:00
|
|
|
assert_not_nil ImmutableDeveloper.find_by_id(1)
|
2009-01-09 18:09:50 -05:00
|
|
|
|
|
|
|
someone = CallbackCancellationDeveloper.find(1)
|
|
|
|
someone.cancel_before_destroy = true
|
|
|
|
assert !someone.destroy
|
|
|
|
assert !someone.after_destroy_called
|
|
|
|
end
|
|
|
|
|
|
|
|
def assert_save_callbacks_not_called(someone)
|
|
|
|
assert !someone.after_save_called
|
|
|
|
assert !someone.after_create_called
|
|
|
|
assert !someone.after_update_called
|
2008-01-18 02:30:42 -05:00
|
|
|
end
|
2009-01-09 18:09:50 -05:00
|
|
|
private :assert_save_callbacks_not_called
|
2005-01-15 12:45:16 -05:00
|
|
|
|
|
|
|
def test_zzz_callback_returning_false # must be run last since we modify CallbackDeveloper
|
|
|
|
david = CallbackDeveloper.find(1)
|
|
|
|
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :returning_false]; return false }
|
|
|
|
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
|
|
|
|
david.save
|
|
|
|
assert_equal [
|
2005-03-13 19:04:14 -05:00
|
|
|
[ :after_find, :string ],
|
|
|
|
[ :after_find, :proc ],
|
|
|
|
[ :after_find, :object ],
|
|
|
|
[ :after_find, :block ],
|
|
|
|
[ :after_initialize, :string ],
|
|
|
|
[ :after_initialize, :proc ],
|
|
|
|
[ :after_initialize, :object ],
|
|
|
|
[ :after_initialize, :block ],
|
2005-01-15 12:45:16 -05:00
|
|
|
[ :before_validation, :string ],
|
|
|
|
[ :before_validation, :proc ],
|
|
|
|
[ :before_validation, :object ],
|
|
|
|
[ :before_validation, :block ],
|
|
|
|
[ :before_validation, :returning_false ]
|
|
|
|
], david.history
|
|
|
|
end
|
2008-01-18 02:30:42 -05:00
|
|
|
|
2007-01-23 22:04:48 -05:00
|
|
|
def test_inheritence_of_callbacks
|
|
|
|
parent = ParentDeveloper.new
|
|
|
|
assert !parent.after_save_called
|
|
|
|
parent.save
|
|
|
|
assert parent.after_save_called
|
2008-01-18 02:30:42 -05:00
|
|
|
|
2007-01-23 22:04:48 -05:00
|
|
|
child = ChildDeveloper.new
|
|
|
|
assert !child.after_save_called
|
|
|
|
child.save
|
|
|
|
assert child.after_save_called
|
|
|
|
end
|
2008-01-18 02:30:42 -05:00
|
|
|
|
2005-01-01 11:14:15 -05:00
|
|
|
end
|