mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Dirty typecasts attribute values before comparison, if possible. Closes #11464 [Russell Norris, mroch]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@9139 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
eece9b4e8a
commit
81286f8587
2 changed files with 61 additions and 59 deletions
|
@ -91,7 +91,12 @@ module ActiveRecord
|
||||||
old = read_attribute(attr)
|
old = read_attribute(attr)
|
||||||
|
|
||||||
# Remember the original value if it's different.
|
# Remember the original value if it's different.
|
||||||
changed_attributes[attr] = old unless old == value
|
typecasted = if column = column_for_attribute(attr)
|
||||||
|
column.type_cast(value)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
changed_attributes[attr] = old unless old == typecasted
|
||||||
end
|
end
|
||||||
|
|
||||||
# Carry on.
|
# Carry on.
|
||||||
|
|
|
@ -1,80 +1,77 @@
|
||||||
require 'cases/helper'
|
require 'cases/helper'
|
||||||
|
require 'models/topic' # For booleans
|
||||||
|
require 'models/pirate' # For timestamps
|
||||||
|
|
||||||
# Stub out an AR-alike.
|
class Pirate # Just reopening it, not defining it
|
||||||
class DirtyTestSubject
|
attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
|
||||||
def self.table_name; 'people' end
|
attr_accessor :changes_detected_in_after_update # Actual changes
|
||||||
def self.primary_key; 'id' end
|
|
||||||
def self.attribute_method_suffix(*suffixes) suffixes end
|
|
||||||
|
|
||||||
def initialize(attrs = {}) @attributes = attrs end
|
after_update :check_changes
|
||||||
|
|
||||||
def save
|
private
|
||||||
changed_attributes.clear
|
# after_save/update in sweepers, observers, and the model itself
|
||||||
|
# can end up checking dirty status and acting on the results
|
||||||
|
def check_changes
|
||||||
|
if self.changed?
|
||||||
|
self.detected_changes_in_after_update = true
|
||||||
|
self.changes_detected_in_after_update = self.changes
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :save!, :save
|
|
||||||
|
|
||||||
def name; read_attribute('name') end
|
|
||||||
def name=(value); write_attribute('name', value) end
|
|
||||||
def name_was; attribute_was('name') end
|
|
||||||
def name_change; attribute_change('name') end
|
|
||||||
def name_changed?; attribute_changed?('name') end
|
|
||||||
|
|
||||||
private
|
|
||||||
def define_read_methods; nil end
|
|
||||||
|
|
||||||
def read_attribute(attr)
|
|
||||||
@attributes[attr]
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_attribute(attr, value)
|
|
||||||
@attributes[attr] = value
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Include the module after the class is all set up.
|
|
||||||
DirtyTestSubject.module_eval { include ActiveRecord::Dirty }
|
|
||||||
|
|
||||||
|
|
||||||
class DirtyTest < Test::Unit::TestCase
|
class DirtyTest < Test::Unit::TestCase
|
||||||
def test_attribute_changes
|
def test_attribute_changes
|
||||||
# New record - no changes.
|
# New record - no changes.
|
||||||
person = DirtyTestSubject.new
|
pirate = Pirate.new
|
||||||
assert !person.name_changed?
|
assert !pirate.catchphrase_changed?
|
||||||
assert_nil person.name_change
|
assert_nil pirate.catchphrase_change
|
||||||
|
|
||||||
# Change name.
|
# Change catchphrase.
|
||||||
person.name = 'a'
|
pirate.catchphrase = 'arrr'
|
||||||
assert person.name_changed?
|
assert pirate.catchphrase_changed?
|
||||||
assert_nil person.name_was
|
assert_nil pirate.catchphrase_was
|
||||||
assert_equal [nil, 'a'], person.name_change
|
assert_equal [nil, 'arrr'], pirate.catchphrase_change
|
||||||
|
|
||||||
# Saved - no changes.
|
# Saved - no changes.
|
||||||
person.save!
|
pirate.save!
|
||||||
assert !person.name_changed?
|
assert !pirate.catchphrase_changed?
|
||||||
assert_nil person.name_change
|
assert_nil pirate.catchphrase_change
|
||||||
|
|
||||||
# Same value - no changes.
|
# Same value - no changes.
|
||||||
person.name = 'a'
|
pirate.catchphrase = 'arrr'
|
||||||
assert !person.name_changed?
|
assert !pirate.catchphrase_changed?
|
||||||
assert_nil person.name_change
|
assert_nil pirate.catchphrase_change
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Rewritten from original tests to use AR
|
||||||
def test_object_should_be_changed_if_any_attribute_is_changed
|
def test_object_should_be_changed_if_any_attribute_is_changed
|
||||||
person = DirtyTestSubject.new
|
pirate = Pirate.new
|
||||||
assert !person.changed?
|
assert !pirate.changed?
|
||||||
assert_equal [], person.changed
|
assert_equal [], pirate.changed
|
||||||
assert_equal Hash.new, person.changes
|
assert_equal Hash.new, pirate.changes
|
||||||
|
|
||||||
person.name = 'a'
|
pirate.catchphrase = 'arrr'
|
||||||
assert person.changed?
|
assert pirate.changed?
|
||||||
assert_nil person.name_was
|
assert_nil pirate.catchphrase_was
|
||||||
assert_equal %w(name), person.changed
|
assert_equal %w(catchphrase), pirate.changed
|
||||||
assert_equal({'name' => [nil, 'a']}, person.changes)
|
assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
|
||||||
|
|
||||||
person.save
|
pirate.save
|
||||||
assert !person.changed?
|
assert !pirate.changed?
|
||||||
assert_equal [], person.changed
|
assert_equal [], pirate.changed
|
||||||
assert_equal({}, person.changes)
|
assert_equal Hash.new, pirate.changes
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_attribute_should_be_compared_with_type_cast
|
||||||
|
topic = Topic.new
|
||||||
|
assert topic.approved?
|
||||||
|
assert !topic.approved_changed?
|
||||||
|
|
||||||
|
# Coming from web form.
|
||||||
|
params = {:topic => {:approved => 1}}
|
||||||
|
# In the controller.
|
||||||
|
topic.attributes = params[:topic]
|
||||||
|
assert topic.approved?
|
||||||
|
assert !topic.approved_changed?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue