diff options
author | Matthew Draper <matthew@trebex.net> | 2014-06-14 05:51:13 +0930 |
---|---|---|
committer | Matthew Draper <matthew@trebex.net> | 2014-06-14 05:51:13 +0930 |
commit | 49fee3d271e52a44a7bc7fcbbcb00792b613b7df (patch) | |
tree | 7a2ffd1363c276f139e45f51cc1690ab69eb3bdb /activerecord/lib/active_record/attribute_methods | |
parent | 1dcb8e997e388cecc75d141812303d42c79a8481 (diff) | |
parent | 4bf8ffc6516312e68fb0d2b4ac97505f8d0cf192 (diff) | |
download | rails-49fee3d271e52a44a7bc7fcbbcb00792b613b7df.tar.gz rails-49fee3d271e52a44a7bc7fcbbcb00792b613b7df.tar.bz2 rails-49fee3d271e52a44a7bc7fcbbcb00792b613b7df.zip |
Merge pull request #15674 from sgrif/sg-mutable-attributes
Detect in-place changes on mutable AR attributes
Diffstat (limited to 'activerecord/lib/active_record/attribute_methods')
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/dirty.rb | 83 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/serialization.rb | 17 |
2 files changed, 77 insertions, 23 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index be438aba95..6a5c057384 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -38,12 +38,39 @@ module ActiveRecord end end - def initialize_dup(other) # :nodoc: - super - init_changed_attributes - end + def initialize_dup(other) # :nodoc: + super + init_changed_attributes + end + + def changed? + super || changed_in_place.any? + end + + def changed + super | changed_in_place + end + + def attribute_changed?(attr_name, options = {}) + result = super + # We can't change "from" something in place. Only setters can define + # "from" and "to" + result ||= changed_in_place?(attr_name) unless options.key?(:from) + result + end + + def changes_applied + super + store_original_raw_attributes + end + + def reset_changes + super + original_raw_attributes.clear + end + + private - private def initialize_internals_callback super init_changed_attributes @@ -65,11 +92,20 @@ module ActiveRecord old_value = old_attribute_value(attr) - result = super(attr, value) + result = super + store_original_raw_attribute(attr) save_changed_attribute(attr, old_value) result end + def raw_write_attribute(attr, value) + attr = attr.to_s + + result = super + original_raw_attributes[attr] = value + result + end + def save_changed_attribute(attr, old_value) if attribute_changed?(attr) changed_attributes.delete(attr) unless _field_changed?(attr, old_value) @@ -105,6 +141,41 @@ module ActiveRecord raw_value = read_attribute_before_type_cast(attr) column_for_attribute(attr).changed?(old_value, new_value, raw_value) end + + def changed_in_place + self.class.attribute_names.select do |attr_name| + changed_in_place?(attr_name) + end + end + + def changed_in_place?(attr_name) + type = type_for_attribute(attr_name) + old_value = original_raw_attribute(attr_name) + value = read_attribute(attr_name) + type.changed_in_place?(old_value, value) + end + + def original_raw_attribute(attr_name) + original_raw_attributes.fetch(attr_name) do + read_attribute_before_type_cast(attr_name) + end + end + + def original_raw_attributes + @original_raw_attributes ||= {} + end + + def store_original_raw_attribute(attr_name) + type = type_for_attribute(attr_name) + value = type.type_cast_for_database(read_attribute(attr_name)) + original_raw_attributes[attr_name] = value + end + + def store_original_raw_attributes + attribute_names.each do |attr| + store_original_raw_attribute(attr) + end + end end end end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 60debb7d18..cec50f62a3 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -50,8 +50,6 @@ module ActiveRecord # serialize :preferences, Hash # end def serialize(attr_name, class_name_or_coder = Object) - include Behavior - coder = if [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) } class_name_or_coder else @@ -67,21 +65,6 @@ module ActiveRecord self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder) end end - - - # This is only added to the model when serialize is called, which - # ensures we do not make things slower when serialization is not used. - module Behavior # :nodoc: - extend ActiveSupport::Concern - - def should_record_timestamps? - super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?) - end - - def keys_for_partial_write - super | (attributes.keys & self.class.serialized_attributes.keys) - end - end end end end |