diff options
author | Sean Griffin <sean@seantheprogrammer.com> | 2015-09-24 13:43:33 -0600 |
---|---|---|
committer | Sean Griffin <sean@seantheprogrammer.com> | 2015-09-24 14:06:59 -0600 |
commit | 8a811c83aa386e4ad26553c913d1a450d8a98d5f (patch) | |
tree | 38a4ccfea032b37560804bcbd4222569f6d25356 /activerecord/lib | |
parent | 8e633e505880755e7e366ccec2210bbe2b5436e7 (diff) | |
download | rails-8a811c83aa386e4ad26553c913d1a450d8a98d5f.tar.gz rails-8a811c83aa386e4ad26553c913d1a450d8a98d5f.tar.bz2 rails-8a811c83aa386e4ad26553c913d1a450d8a98d5f.zip |
Encapsulate a lot of the logic from `Dirty` in an object
In order to improve the performance of dirty checking, we're going to
need to duplicate all of the `previous_` methods in Active Model.
However, these methods are basically the same as their non-previous
counterparts, but comparing `@original_attributes` to
`@previous_original_attributes` instead of `@attributes` and
`@original_attributes`. This will help reduce that duplication.
Diffstat (limited to 'activerecord/lib')
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/dirty.rb | 33 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_mutation_tracker.rb | 55 | ||||
-rw-r--r-- | activerecord/lib/active_record/persistence.rb | 2 |
3 files changed, 65 insertions, 25 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index fd0a3cd313..a439683185 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/module/attribute_accessors' +require 'active_record/attribute_mutation_tracker' module ActiveRecord module AttributeMethods @@ -40,12 +41,13 @@ module ActiveRecord def init_internals super - @original_attributes = @attributes.dup + @mutation_tracker = AttributeMutationTracker.new(@attributes, @attributes.dup) end def initialize_dup(other) # :nodoc: super - @original_attributes = self.class._default_attributes.dup + @mutation_tracker = AttributeMutationTracker.new(@attributes, + self.class._default_attributes.dup) end def changes_applied @@ -77,7 +79,7 @@ module ActiveRecord if defined?(@cached_changed_attributes) @cached_changed_attributes else - calculate_changed_attributes.freeze + super.reverse_merge(@mutation_tracker.changed_values).freeze end end @@ -88,20 +90,17 @@ module ActiveRecord end def attribute_changed_in_place?(attr_name) - original_database_value = @original_attributes[attr_name].value_before_type_cast - @attributes[attr_name].changed_in_place_from?(original_database_value) + @mutation_tracker.changed_in_place?(attr_name) end private def changes_include?(attr_name) - attr_name = attr_name.to_s - super || attribute_modified?(attr_name) || attribute_changed_in_place?(attr_name) + super || @mutation_tracker.changed?(attr_name) end def clear_attribute_change(attr_name) - attr_name = attr_name.to_s - @original_attributes[attr_name] = @attributes[attr_name].dup + @mutation_tracker.forget_change(attr_name) end def _update_record(*) @@ -116,22 +115,8 @@ module ActiveRecord changed & self.class.column_names end - def attribute_modified?(attr_name) - @attributes[attr_name].changed_from?(@original_attributes.fetch_value(attr_name)) - end - def store_original_attributes - @original_attributes = @attributes.map do |attr| - attr.with_value_from_database(attr.value_for_database) - end - end - - def calculate_changed_attributes - attribute_names.each_with_object({}.with_indifferent_access) do |attr_name, result| - if changes_include?(attr_name) - result[attr_name] = @original_attributes.fetch_value(attr_name) - end - end + @mutation_tracker = @mutation_tracker.now_tracking(@attributes) end def cache_changed_attributes diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb new file mode 100644 index 0000000000..e4be5c5524 --- /dev/null +++ b/activerecord/lib/active_record/attribute_mutation_tracker.rb @@ -0,0 +1,55 @@ +module ActiveRecord + class AttributeMutationTracker + def initialize(attributes, original_attributes) + @attributes = attributes + @original_attributes = original_attributes + end + + def changed_values + attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result| + if changed?(attr_name) + result[attr_name] = original_attributes.fetch_value(attr_name) + end + end + end + + def changed?(attr_name) + attr_name = attr_name.to_s + modified?(attr_name) || changed_in_place?(attr_name) + end + + def changed_in_place?(attr_name) + original_database_value = original_attributes[attr_name].value_before_type_cast + attributes[attr_name].changed_in_place_from?(original_database_value) + end + + def forget_change(attr_name) + attr_name = attr_name.to_s + original_attributes[attr_name] = attributes[attr_name].dup + end + + def now_tracking(new_attributes) + AttributeMutationTracker.new(new_attributes, clean_copy_of(new_attributes)) + end + + protected + + attr_reader :attributes, :original_attributes + + private + + def attr_names + attributes.keys + end + + def modified?(attr_name) + attributes[attr_name].changed_from?(original_attributes.fetch_value(attr_name)) + end + + def clean_copy_of(attributes) + attributes.map do |attr| + attr.with_value_from_database(attr.value_for_database) + end + end + end +end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 1b7ee0bd38..7b53f6e5a0 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -211,7 +211,7 @@ module ActiveRecord def becomes(klass) became = klass.new became.instance_variable_set("@attributes", @attributes) - became.instance_variable_set("@original_attributes", @original_attributes) + became.instance_variable_set("@mutation_tracker", @mutation_tracker) became.instance_variable_set("@changed_attributes", attributes_changed_by_setter) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) |