aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorSean Griffin <sean@seantheprogrammer.com>2015-09-24 13:43:33 -0600
committerSean Griffin <sean@seantheprogrammer.com>2015-09-24 14:06:59 -0600
commit8a811c83aa386e4ad26553c913d1a450d8a98d5f (patch)
tree38a4ccfea032b37560804bcbd4222569f6d25356 /activerecord/lib
parent8e633e505880755e7e366ccec2210bbe2b5436e7 (diff)
downloadrails-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.rb33
-rw-r--r--activerecord/lib/active_record/attribute_mutation_tracker.rb55
-rw-r--r--activerecord/lib/active_record/persistence.rb2
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?)