lib/paper_trail/events/base.rb in paper_trail-10.2.0 vs lib/paper_trail/events/base.rb in paper_trail-10.2.1
- old
+ new
@@ -57,20 +57,35 @@
@record.attribute_changed?(attr_name.to_s)
end
end
# @api private
- def attributes_before_change(is_touch)
- Hash[@record.attributes.map do |k, v|
- if @record.class.column_names.include?(k)
- [k, attribute_in_previous_version(k, is_touch)]
- else
- [k, v]
+ def nonskipped_attributes_before_change(is_touch)
+ cache_changed_attributes do
+ record_attributes = @record.attributes.except(*@record.paper_trail_options[:skip])
+
+ record_attributes.each_key do |k|
+ if @record.class.column_names.include?(k)
+ record_attributes[k] = attribute_in_previous_version(k, is_touch)
+ end
end
- end]
+ end
end
+ # Rails 5.1 changed the API of `ActiveRecord::Dirty`.
+ # @api private
+ def cache_changed_attributes
+ if RAILS_GTE_5_1
+ # Everything works fine as it is
+ yield
+ else
+ # Any particular call to `changed_attributes` produces the huge memory allocation.
+ # Lets use the generic AR workaround for that.
+ @record.send(:cache_changed_attributes) { yield }
+ end
+ end
+
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
# https://github.com/paper-trail-gem/paper_trail/pull/899
#
# Event can be any of the three (create, update, destroy).
#
@@ -106,22 +121,26 @@
(changed_in_latest_version - ignore) - skip
end
# @api private
def changed_in_latest_version
- changes_in_latest_version.keys
+ # Memoized to reduce memory usage
+ @changed_in_latest_version ||= changes_in_latest_version.keys
end
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
# https://github.com/paper-trail-gem/paper_trail/pull/899
#
# @api private
def changes_in_latest_version
- if @in_after_callback && RAILS_GTE_5_1
- @record.saved_changes
- else
- @record.changes
+ # Memoized to reduce memory usage
+ @changes_in_latest_version ||= begin
+ if @in_after_callback && RAILS_GTE_5_1
+ @record.saved_changes
+ else
+ @record.changes
+ end
end
end
# An attributed is "ignored" if it is listed in the `:ignore` option
# and/or the `:skip` option. Returns true if an ignored attribute has
@@ -198,29 +217,31 @@
}
end
# @api private
def notably_changed
- only = @record.paper_trail_options[:only].dup
- # Remove Hash arguments and then evaluate whether the attributes (the
- # keys of the hash) should also get pushed into the collection.
- only.delete_if do |obj|
- obj.is_a?(Hash) &&
- obj.each { |attr, condition|
- only << attr if condition.respond_to?(:call) && condition.call(@record)
- }
+ # Memoized to reduce memory usage
+ @notably_changed ||= begin
+ only = @record.paper_trail_options[:only].dup
+ # Remove Hash arguments and then evaluate whether the attributes (the
+ # keys of the hash) should also get pushed into the collection.
+ only.delete_if do |obj|
+ obj.is_a?(Hash) &&
+ obj.each { |attr, condition|
+ only << attr if condition.respond_to?(:call) && condition.call(@record)
+ }
+ end
+ only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
end
- only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
end
# Returns hash of attributes (with appropriate attributes serialized),
# omitting attributes to be skipped.
#
# @api private
def object_attrs_for_paper_trail(is_touch)
- attrs = attributes_before_change(is_touch).
- except(*@record.paper_trail_options[:skip])
+ attrs = nonskipped_attributes_before_change(is_touch)
AttributeSerializers::ObjectAttribute.new(@record.class).serialize(attrs)
attrs
end
# @api private
@@ -235,13 +256,18 @@
# a postgres `json` column, then a hash can be used in the assignment,
# otherwise the column is a `text` column, and we must perform the
# serialization here, using `PaperTrail.serializer`.
#
# @api private
+ # @param changes HashWithIndifferentAccess
def recordable_object_changes(changes)
if PaperTrail.config.object_changes_adapter&.respond_to?(:diff)
- changes = PaperTrail.config.object_changes_adapter.diff(changes)
+ # We'd like to avoid the `to_hash` here, because it increases memory
+ # usage, but that would be a breaking change because
+ # `object_changes_adapter` expects a plain `Hash`, not a
+ # `HashWithIndifferentAccess`.
+ changes = PaperTrail.config.object_changes_adapter.diff(changes.to_hash)
end
if @record.class.paper_trail.version_class.object_changes_col_is_json?
changes
else
@@ -282,10 +308,13 @@
# @api private
def serialize_object_changes(changes)
AttributeSerializers::ObjectChangesAttribute.
new(@record.class).
serialize(changes)
- changes.to_hash
+
+ # We'd like to convert this `HashWithIndifferentAccess` to a plain
+ # `Hash`, but we don't, to save memory.
+ changes
end
end
end
end