mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Don't calculate all in-place changes to determine if attribute_changed?
Calling `changed_attributes` will ultimately check if every mutable attribute has changed in place. Since this gets called whenever an attribute is assigned, it's extremely slow. Instead, we can avoid this calculation until we actually need it. Fixes #18029
This commit is contained in:
parent
849274316d
commit
18ae0656f5
3 changed files with 24 additions and 1 deletions
|
@ -170,7 +170,7 @@ module ActiveModel
|
|||
|
||||
# Handle <tt>*_changed?</tt> for +method_missing+.
|
||||
def attribute_changed?(attr, options = {}) #:nodoc:
|
||||
result = changed_attributes.include?(attr)
|
||||
result = changes_include?(attr)
|
||||
result &&= options[:to] == __send__(attr) if options.key?(:to)
|
||||
result &&= options[:from] == changed_attributes[attr] if options.key?(:from)
|
||||
result
|
||||
|
@ -188,6 +188,10 @@ module ActiveModel
|
|||
|
||||
private
|
||||
|
||||
def changes_include?(attr_name)
|
||||
attributes_changed_by_setter.include?(attr_name)
|
||||
end
|
||||
|
||||
# Removes current changes and makes them accessible through +previous_changes+.
|
||||
def changes_applied # :doc:
|
||||
@previously_changed = changes
|
||||
|
|
|
@ -76,6 +76,10 @@ module ActiveRecord
|
|||
|
||||
private
|
||||
|
||||
def changes_include?(attr_name)
|
||||
super || attribute_changed_in_place?(attr_name)
|
||||
end
|
||||
|
||||
def calculate_changes_from_defaults
|
||||
@changed_attributes = nil
|
||||
self.class.column_defaults.each do |attr, orig_value|
|
||||
|
|
|
@ -698,6 +698,21 @@ class DirtyTest < ActiveRecord::TestCase
|
|||
assert binary.changed?
|
||||
end
|
||||
|
||||
test "attribute_changed? doesn't compute in-place changes for unrelated attributes" do
|
||||
test_type_class = Class.new(ActiveRecord::Type::Value) do
|
||||
define_method(:changed_in_place?) do |*|
|
||||
raise
|
||||
end
|
||||
end
|
||||
klass = Class.new(ActiveRecord::Base) do
|
||||
self.table_name = 'people'
|
||||
attribute :foo, test_type_class.new
|
||||
end
|
||||
|
||||
model = klass.new(first_name: "Jim")
|
||||
assert model.first_name_changed?
|
||||
end
|
||||
|
||||
private
|
||||
def with_partial_writes(klass, on = true)
|
||||
old = klass.partial_writes?
|
||||
|
|
Loading…
Reference in a new issue