diff options
author | Sean Griffin <sean@thoughtbot.com> | 2014-06-07 08:56:27 -0600 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2014-06-07 11:46:58 -0600 |
commit | 368cca51d25fa15c0ddc8743b5904c4d5ce93e74 (patch) | |
tree | 3ee23e482c0167811cf8bb04c46a0cd6aa575911 /activerecord/lib/active_record/attribute_methods/dirty.rb | |
parent | 2f5fab8d5af6dfe59e73fcf3d8d9902715b4284b (diff) | |
download | rails-368cca51d25fa15c0ddc8743b5904c4d5ce93e74.tar.gz rails-368cca51d25fa15c0ddc8743b5904c4d5ce93e74.tar.bz2 rails-368cca51d25fa15c0ddc8743b5904c4d5ce93e74.zip |
Do not type cast twice on attribute assignment
The definition of `write_attribute` in dirty checking ultimately leads
to the columns calling `type_cast` on the value to perform the
comparison. However, this is a potentially expensive computation that we
cache when it occurs in `read_attribute`. The only case that we need the
non-type-cast form is for numeric, so we pass that through as well
(something I'm looking to remove in the future).
This also reduces the number of places that manually access various
stages in an attribute's type casting lifecycle, which will aid in one
of the larger refactorings that I'm working on.
Diffstat (limited to 'activerecord/lib/active_record/attribute_methods/dirty.rb')
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/dirty.rb | 31 |
1 files changed, 20 insertions, 11 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 4e32b78e34..be438aba95 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -55,7 +55,7 @@ module ActiveRecord # optimistic locking) won't get written unless they get marked as changed self.class.columns.each do |c| attr, orig_value = c.name, c.default - changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @raw_attributes[attr]) + changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value) end end @@ -63,19 +63,26 @@ module ActiveRecord def write_attribute(attr, value) attr = attr.to_s - save_changed_attribute(attr, value) + old_value = old_attribute_value(attr) - super(attr, value) + result = super(attr, value) + save_changed_attribute(attr, old_value) + result end - def save_changed_attribute(attr, value) - # The attribute already has an unsaved change. + def save_changed_attribute(attr, old_value) if attribute_changed?(attr) - old = changed_attributes[attr] - changed_attributes.delete(attr) unless _field_changed?(attr, old, value) + changed_attributes.delete(attr) unless _field_changed?(attr, old_value) else - old = clone_attribute_value(:read_attribute, attr) - changed_attributes[attr] = old if _field_changed?(attr, old, value) + changed_attributes[attr] = old_value if _field_changed?(attr, old_value) + end + end + + def old_attribute_value(attr) + if attribute_changed?(attr) + changed_attributes[attr] + else + clone_attribute_value(:read_attribute, attr) end end @@ -93,8 +100,10 @@ module ActiveRecord changed end - def _field_changed?(attr, old, value) - column_for_attribute(attr).changed?(old, value) + def _field_changed?(attr, old_value) + new_value = read_attribute(attr) + raw_value = read_attribute_before_type_cast(attr) + column_for_attribute(attr).changed?(old_value, new_value, raw_value) end end end |