mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #6936 from jfoley/callbacks
Fix collisions with before and after validation callbacks.
This commit is contained in:
commit
1dbe4baef5
4 changed files with 78 additions and 2 deletions
|
@ -56,7 +56,8 @@ module ActiveModel
|
|||
options = args.last
|
||||
if options.is_a?(Hash) && options[:on]
|
||||
options[:if] = Array(options[:if])
|
||||
options[:if].unshift("self.validation_context == :#{options[:on]}")
|
||||
options[:on] = Array(options[:on])
|
||||
options[:if].unshift("#{options[:on]}.include? self.validation_context")
|
||||
end
|
||||
set_callback(:validation, :before, *args, &block)
|
||||
end
|
||||
|
@ -92,7 +93,10 @@ module ActiveModel
|
|||
options = args.extract_options!
|
||||
options[:prepend] = true
|
||||
options[:if] = Array(options[:if])
|
||||
options[:if].unshift("self.validation_context == :#{options[:on]}") if options[:on]
|
||||
if options[:on]
|
||||
options[:on] = Array(options[:on])
|
||||
options[:if].unshift("#{options[:on]}.include? self.validation_context")
|
||||
end
|
||||
set_callback(:validation, :after, *(args << options), &block)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
## Rails 4.0.0 (unreleased) ##
|
||||
|
||||
* Allow before and after validations to take an array of lifecycle events
|
||||
|
||||
*John Foley*
|
||||
|
||||
* Support for specifying transaction isolation level
|
||||
|
||||
If your database supports setting the isolation level for a transaction, you can set
|
||||
|
|
|
@ -137,6 +137,32 @@ class OnCallbacksDeveloper < ActiveRecord::Base
|
|||
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'
|
||||
|
||||
|
@ -285,6 +311,17 @@ class CallbacksTest < ActiveRecord::TestCase
|
|||
], 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
|
||||
|
@ -344,6 +381,18 @@ class CallbacksTest < ActiveRecord::TestCase
|
|||
], 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
|
||||
|
|
|
@ -995,6 +995,25 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
```
|
||||
|
||||
Callbacks can also be registered to only fire on certain lifecycle events:
|
||||
<ruby>
|
||||
class User < ActiveRecord::Base
|
||||
before_validation :normalize_name, :on => :create
|
||||
|
||||
# :on takes an array as well
|
||||
after_validation :set_location, :on => [ :create, :update ]
|
||||
|
||||
protected
|
||||
def normalize_name
|
||||
self.name = self.name.downcase.titleize
|
||||
end
|
||||
|
||||
def set_location
|
||||
self.location = LocationService.query(self)
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
||||
It is considered good practice to declare callback methods as protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation.
|
||||
|
||||
Available Callbacks
|
||||
|
|
Loading…
Reference in a new issue