mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
9617db2078
The current code base is not uniform. After some discussion, we have chosen to go with double quotes by default.
636 lines
21 KiB
Ruby
636 lines
21 KiB
Ruby
require "cases/helper"
|
|
require "models/developer"
|
|
require "models/computer"
|
|
|
|
class CallbackDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
class << self
|
|
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) do
|
|
self.history << [callback_method, :method]
|
|
end
|
|
send(callback_method, :"#{callback_method}")
|
|
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|
|
|
next if callback_method.to_s.start_with?("around_")
|
|
define_callback_method(callback_method)
|
|
ActiveSupport::Deprecation.silence { send(callback_method, callback_string(callback_method)) }
|
|
send(callback_method, callback_proc(callback_method))
|
|
send(callback_method, callback_object(callback_method))
|
|
send(callback_method) { |model| model.history << [callback_method, :block] }
|
|
end
|
|
|
|
def history
|
|
@history ||= []
|
|
end
|
|
end
|
|
|
|
class CallbackDeveloperWithFalseValidation < CallbackDeveloper
|
|
before_validation proc { |model| model.history << [:before_validation, :returning_false]; false }
|
|
before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
|
|
end
|
|
|
|
class CallbackDeveloperWithHaltedValidation < CallbackDeveloper
|
|
before_validation proc { |model| model.history << [:before_validation, :throwing_abort]; throw(:abort) }
|
|
before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
|
|
end
|
|
|
|
class ParentDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
attr_accessor :after_save_called
|
|
before_validation {|record| record.after_save_called = true}
|
|
end
|
|
|
|
class ChildDeveloper < ParentDeveloper
|
|
|
|
end
|
|
|
|
class ImmutableDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
validates_inclusion_of :salary, :in => 50000..200000
|
|
|
|
before_save :cancel
|
|
before_destroy :cancel
|
|
|
|
private
|
|
def cancel
|
|
false
|
|
end
|
|
end
|
|
|
|
class DeveloperWithCanceledCallbacks < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
validates_inclusion_of :salary, in: 50000..200000
|
|
|
|
before_save :cancel
|
|
before_destroy :cancel
|
|
|
|
private
|
|
def cancel
|
|
throw(:abort)
|
|
end
|
|
end
|
|
|
|
class OnCallbacksDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
before_validation { history << :before_validation }
|
|
before_validation(:on => :create){ history << :before_validation_on_create }
|
|
before_validation(:on => :update){ history << :before_validation_on_update }
|
|
|
|
validate do
|
|
history << :validate
|
|
end
|
|
|
|
after_validation { history << :after_validation }
|
|
after_validation(:on => :create){ history << :after_validation_on_create }
|
|
after_validation(:on => :update){ history << :after_validation_on_update }
|
|
|
|
def history
|
|
@history ||= []
|
|
end
|
|
end
|
|
|
|
class ContextualCallbacksDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
before_validation { history << :before_validation }
|
|
before_validation :before_validation_on_create_and_update, :on => [ :create, :update ]
|
|
|
|
validate do
|
|
history << :validate
|
|
end
|
|
|
|
after_validation { history << :after_validation }
|
|
after_validation :after_validation_on_create_and_update, :on => [ :create, :update ]
|
|
|
|
def before_validation_on_create_and_update
|
|
history << "before_validation_on_#{self.validation_context}".to_sym
|
|
end
|
|
|
|
def after_validation_on_create_and_update
|
|
history << "after_validation_on_#{self.validation_context}".to_sym
|
|
end
|
|
|
|
def history
|
|
@history ||= []
|
|
end
|
|
end
|
|
|
|
class CallbackCancellationDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
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
|
|
|
|
before_save {defined?(@cancel_before_save) ? !@cancel_before_save : false}
|
|
before_create { !@cancel_before_create }
|
|
before_update { !@cancel_before_update }
|
|
before_destroy { !@cancel_before_destroy }
|
|
|
|
after_save { @after_save_called = true }
|
|
after_update { @after_update_called = true }
|
|
after_create { @after_create_called = true }
|
|
after_destroy { @after_destroy_called = true }
|
|
end
|
|
|
|
class CallbackHaltedDeveloper < ActiveRecord::Base
|
|
self.table_name = "developers"
|
|
|
|
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
|
|
|
|
before_save { throw(:abort) if defined?(@cancel_before_save) }
|
|
before_create { throw(:abort) if @cancel_before_create }
|
|
before_update { throw(:abort) if @cancel_before_update }
|
|
before_destroy { throw(:abort) if @cancel_before_destroy }
|
|
|
|
after_save { @after_save_called = true }
|
|
after_update { @after_update_called = true }
|
|
after_create { @after_create_called = true }
|
|
after_destroy { @after_destroy_called = true }
|
|
end
|
|
|
|
class CallbacksTest < ActiveRecord::TestCase
|
|
fixtures :developers
|
|
|
|
def test_initialize
|
|
david = CallbackDeveloper.new
|
|
assert_equal [
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
], david.history
|
|
end
|
|
|
|
def test_find
|
|
david = CallbackDeveloper.find(1)
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
], david.history
|
|
end
|
|
|
|
def test_new_valid?
|
|
david = CallbackDeveloper.new
|
|
david.valid?
|
|
assert_equal [
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_validation, :method ],
|
|
[ :before_validation, :string ],
|
|
[ :before_validation, :proc ],
|
|
[ :before_validation, :object ],
|
|
[ :before_validation, :block ],
|
|
[ :after_validation, :method ],
|
|
[ :after_validation, :string ],
|
|
[ :after_validation, :proc ],
|
|
[ :after_validation, :object ],
|
|
[ :after_validation, :block ],
|
|
], david.history
|
|
end
|
|
|
|
def test_existing_valid?
|
|
david = CallbackDeveloper.find(1)
|
|
david.valid?
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_validation, :method ],
|
|
[ :before_validation, :string ],
|
|
[ :before_validation, :proc ],
|
|
[ :before_validation, :object ],
|
|
[ :before_validation, :block ],
|
|
[ :after_validation, :method ],
|
|
[ :after_validation, :string ],
|
|
[ :after_validation, :proc ],
|
|
[ :after_validation, :object ],
|
|
[ :after_validation, :block ],
|
|
], david.history
|
|
end
|
|
|
|
def test_create
|
|
david = CallbackDeveloper.create("name" => "David", "salary" => 1000000)
|
|
assert_equal [
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_validation, :method ],
|
|
[ :before_validation, :string ],
|
|
[ :before_validation, :proc ],
|
|
[ :before_validation, :object ],
|
|
[ :before_validation, :block ],
|
|
[ :after_validation, :method ],
|
|
[ :after_validation, :string ],
|
|
[ :after_validation, :proc ],
|
|
[ :after_validation, :object ],
|
|
[ :after_validation, :block ],
|
|
[ :before_save, :method ],
|
|
[ :before_save, :string ],
|
|
[ :before_save, :proc ],
|
|
[ :before_save, :object ],
|
|
[ :before_save, :block ],
|
|
[ :before_create, :method ],
|
|
[ :before_create, :string ],
|
|
[ :before_create, :proc ],
|
|
[ :before_create, :object ],
|
|
[ :before_create, :block ],
|
|
[ :after_create, :method ],
|
|
[ :after_create, :string ],
|
|
[ :after_create, :proc ],
|
|
[ :after_create, :object ],
|
|
[ :after_create, :block ],
|
|
[ :after_save, :method ],
|
|
[ :after_save, :string ],
|
|
[ :after_save, :proc ],
|
|
[ :after_save, :object ],
|
|
[ :after_save, :block ],
|
|
[ :after_commit, :block ],
|
|
[ :after_commit, :object ],
|
|
[ :after_commit, :proc ],
|
|
[ :after_commit, :string ],
|
|
[ :after_commit, :method ]
|
|
], david.history
|
|
end
|
|
|
|
def test_validate_on_create
|
|
david = OnCallbacksDeveloper.create("name" => "David", "salary" => 1000000)
|
|
assert_equal [
|
|
:before_validation,
|
|
:before_validation_on_create,
|
|
:validate,
|
|
:after_validation,
|
|
:after_validation_on_create
|
|
], david.history
|
|
end
|
|
|
|
def test_validate_on_contextual_create
|
|
david = ContextualCallbacksDeveloper.create("name" => "David", "salary" => 1000000)
|
|
assert_equal [
|
|
:before_validation,
|
|
:before_validation_on_create,
|
|
:validate,
|
|
:after_validation,
|
|
:after_validation_on_create
|
|
], david.history
|
|
end
|
|
|
|
def test_update
|
|
david = CallbackDeveloper.find(1)
|
|
david.save
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_validation, :method ],
|
|
[ :before_validation, :string ],
|
|
[ :before_validation, :proc ],
|
|
[ :before_validation, :object ],
|
|
[ :before_validation, :block ],
|
|
[ :after_validation, :method ],
|
|
[ :after_validation, :string ],
|
|
[ :after_validation, :proc ],
|
|
[ :after_validation, :object ],
|
|
[ :after_validation, :block ],
|
|
[ :before_save, :method ],
|
|
[ :before_save, :string ],
|
|
[ :before_save, :proc ],
|
|
[ :before_save, :object ],
|
|
[ :before_save, :block ],
|
|
[ :before_update, :method ],
|
|
[ :before_update, :string ],
|
|
[ :before_update, :proc ],
|
|
[ :before_update, :object ],
|
|
[ :before_update, :block ],
|
|
[ :after_update, :method ],
|
|
[ :after_update, :string ],
|
|
[ :after_update, :proc ],
|
|
[ :after_update, :object ],
|
|
[ :after_update, :block ],
|
|
[ :after_save, :method ],
|
|
[ :after_save, :string ],
|
|
[ :after_save, :proc ],
|
|
[ :after_save, :object ],
|
|
[ :after_save, :block ],
|
|
[ :after_commit, :block ],
|
|
[ :after_commit, :object ],
|
|
[ :after_commit, :proc ],
|
|
[ :after_commit, :string ],
|
|
[ :after_commit, :method ]
|
|
], david.history
|
|
end
|
|
|
|
def test_validate_on_update
|
|
david = OnCallbacksDeveloper.find(1)
|
|
david.save
|
|
assert_equal [
|
|
:before_validation,
|
|
:before_validation_on_update,
|
|
:validate,
|
|
:after_validation,
|
|
:after_validation_on_update
|
|
], david.history
|
|
end
|
|
|
|
def test_validate_on_contextual_update
|
|
david = ContextualCallbacksDeveloper.find(1)
|
|
david.save
|
|
assert_equal [
|
|
:before_validation,
|
|
:before_validation_on_update,
|
|
:validate,
|
|
:after_validation,
|
|
:after_validation_on_update
|
|
], david.history
|
|
end
|
|
|
|
def test_destroy
|
|
david = CallbackDeveloper.find(1)
|
|
david.destroy
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_destroy, :method ],
|
|
[ :before_destroy, :string ],
|
|
[ :before_destroy, :proc ],
|
|
[ :before_destroy, :object ],
|
|
[ :before_destroy, :block ],
|
|
[ :after_destroy, :method ],
|
|
[ :after_destroy, :string ],
|
|
[ :after_destroy, :proc ],
|
|
[ :after_destroy, :object ],
|
|
[ :after_destroy, :block ],
|
|
[ :after_commit, :block ],
|
|
[ :after_commit, :object ],
|
|
[ :after_commit, :proc ],
|
|
[ :after_commit, :string ],
|
|
[ :after_commit, :method ]
|
|
], david.history
|
|
end
|
|
|
|
def test_delete
|
|
david = CallbackDeveloper.find(1)
|
|
CallbackDeveloper.delete(david.id)
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
], david.history
|
|
end
|
|
|
|
def test_deprecated_before_save_returning_false
|
|
david = ImmutableDeveloper.find(1)
|
|
assert_deprecated do
|
|
assert david.valid?
|
|
assert !david.save
|
|
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
|
|
assert_equal exc.record, david
|
|
assert_equal "Failed to save the record", exc.message
|
|
end
|
|
|
|
david = ImmutableDeveloper.find(1)
|
|
david.salary = 10_000_000
|
|
assert !david.valid?
|
|
assert !david.save
|
|
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
|
|
|
|
someone = CallbackCancellationDeveloper.find(1)
|
|
someone.cancel_before_save = true
|
|
assert_deprecated do
|
|
assert someone.valid?
|
|
assert !someone.save
|
|
end
|
|
assert_save_callbacks_not_called(someone)
|
|
end
|
|
|
|
def test_deprecated_before_create_returning_false
|
|
someone = CallbackCancellationDeveloper.new
|
|
someone.cancel_before_create = true
|
|
assert_deprecated do
|
|
assert someone.valid?
|
|
assert !someone.save
|
|
end
|
|
assert_save_callbacks_not_called(someone)
|
|
end
|
|
|
|
def test_deprecated_before_update_returning_false
|
|
someone = CallbackCancellationDeveloper.find(1)
|
|
someone.cancel_before_update = true
|
|
assert_deprecated do
|
|
assert someone.valid?
|
|
assert !someone.save
|
|
end
|
|
assert_save_callbacks_not_called(someone)
|
|
end
|
|
|
|
def test_deprecated_before_destroy_returning_false
|
|
david = ImmutableDeveloper.find(1)
|
|
assert_deprecated do
|
|
assert !david.destroy
|
|
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
|
|
assert_equal exc.record, david
|
|
assert_equal "Failed to destroy the record", exc.message
|
|
end
|
|
assert_not_nil ImmutableDeveloper.find_by_id(1)
|
|
|
|
someone = CallbackCancellationDeveloper.find(1)
|
|
someone.cancel_before_destroy = true
|
|
assert_deprecated do
|
|
assert !someone.destroy
|
|
assert_raise(ActiveRecord::RecordNotDestroyed) { someone.destroy! }
|
|
end
|
|
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
|
|
end
|
|
private :assert_save_callbacks_not_called
|
|
|
|
def test_before_create_throwing_abort
|
|
someone = CallbackHaltedDeveloper.new
|
|
someone.cancel_before_create = true
|
|
assert someone.valid?
|
|
assert !someone.save
|
|
assert_save_callbacks_not_called(someone)
|
|
end
|
|
|
|
def test_before_save_throwing_abort
|
|
david = DeveloperWithCanceledCallbacks.find(1)
|
|
assert david.valid?
|
|
assert !david.save
|
|
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
|
|
assert_equal exc.record, david
|
|
|
|
david = DeveloperWithCanceledCallbacks.find(1)
|
|
david.salary = 10_000_000
|
|
assert !david.valid?
|
|
assert !david.save
|
|
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
|
|
|
|
someone = CallbackHaltedDeveloper.find(1)
|
|
someone.cancel_before_save = true
|
|
assert someone.valid?
|
|
assert !someone.save
|
|
assert_save_callbacks_not_called(someone)
|
|
end
|
|
|
|
def test_before_update_throwing_abort
|
|
someone = CallbackHaltedDeveloper.find(1)
|
|
someone.cancel_before_update = true
|
|
assert someone.valid?
|
|
assert !someone.save
|
|
assert_save_callbacks_not_called(someone)
|
|
end
|
|
|
|
def test_before_destroy_throwing_abort
|
|
david = DeveloperWithCanceledCallbacks.find(1)
|
|
assert !david.destroy
|
|
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
|
|
assert_equal exc.record, david
|
|
assert_not_nil ImmutableDeveloper.find_by_id(1)
|
|
|
|
someone = CallbackHaltedDeveloper.find(1)
|
|
someone.cancel_before_destroy = true
|
|
assert !someone.destroy
|
|
assert_raise(ActiveRecord::RecordNotDestroyed) { someone.destroy! }
|
|
assert !someone.after_destroy_called
|
|
end
|
|
|
|
def test_callback_returning_false
|
|
david = CallbackDeveloperWithFalseValidation.find(1)
|
|
assert_deprecated { david.save }
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_validation, :method ],
|
|
[ :before_validation, :string ],
|
|
[ :before_validation, :proc ],
|
|
[ :before_validation, :object ],
|
|
[ :before_validation, :block ],
|
|
[ :before_validation, :returning_false ],
|
|
[ :after_rollback, :block ],
|
|
[ :after_rollback, :object ],
|
|
[ :after_rollback, :proc ],
|
|
[ :after_rollback, :string ],
|
|
[ :after_rollback, :method ],
|
|
], david.history
|
|
end
|
|
|
|
def test_callback_throwing_abort
|
|
david = CallbackDeveloperWithHaltedValidation.find(1)
|
|
david.save
|
|
assert_equal [
|
|
[ :after_find, :method ],
|
|
[ :after_find, :string ],
|
|
[ :after_find, :proc ],
|
|
[ :after_find, :object ],
|
|
[ :after_find, :block ],
|
|
[ :after_initialize, :method ],
|
|
[ :after_initialize, :string ],
|
|
[ :after_initialize, :proc ],
|
|
[ :after_initialize, :object ],
|
|
[ :after_initialize, :block ],
|
|
[ :before_validation, :method ],
|
|
[ :before_validation, :string ],
|
|
[ :before_validation, :proc ],
|
|
[ :before_validation, :object ],
|
|
[ :before_validation, :block ],
|
|
[ :before_validation, :throwing_abort ],
|
|
[ :after_rollback, :block ],
|
|
[ :after_rollback, :object ],
|
|
[ :after_rollback, :proc ],
|
|
[ :after_rollback, :string ],
|
|
[ :after_rollback, :method ],
|
|
], david.history
|
|
end
|
|
|
|
def test_inheritance_of_callbacks
|
|
parent = ParentDeveloper.new
|
|
assert !parent.after_save_called
|
|
parent.save
|
|
assert parent.after_save_called
|
|
|
|
child = ChildDeveloper.new
|
|
assert !child.after_save_called
|
|
child.save
|
|
assert child.after_save_called
|
|
end
|
|
|
|
end
|