mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
5090a64aa3
It is almost no longer used in Active Record.
181 lines
3.8 KiB
Ruby
181 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "active_support/core_ext/hash/indifferent_access"
|
|
require "active_support/core_ext/object/duplicable"
|
|
|
|
module ActiveModel
|
|
class AttributeMutationTracker # :nodoc:
|
|
OPTION_NOT_GIVEN = Object.new
|
|
|
|
def initialize(attributes)
|
|
@attributes = attributes
|
|
end
|
|
|
|
def changed_attribute_names
|
|
attr_names.select { |attr_name| changed?(attr_name) }
|
|
end
|
|
|
|
def changed_values
|
|
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
|
|
if changed?(attr_name)
|
|
result[attr_name] = original_value(attr_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
def changes
|
|
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
|
|
if change = change_to_attribute(attr_name)
|
|
result.merge!(attr_name => change)
|
|
end
|
|
end
|
|
end
|
|
|
|
def change_to_attribute(attr_name)
|
|
if changed?(attr_name)
|
|
[original_value(attr_name), fetch_value(attr_name)]
|
|
end
|
|
end
|
|
|
|
def any_changes?
|
|
attr_names.any? { |attr| changed?(attr) }
|
|
end
|
|
|
|
def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
|
|
attribute_changed?(attr_name) &&
|
|
(OPTION_NOT_GIVEN == from || original_value(attr_name) == from) &&
|
|
(OPTION_NOT_GIVEN == to || fetch_value(attr_name) == to)
|
|
end
|
|
|
|
def changed_in_place?(attr_name)
|
|
attributes[attr_name].changed_in_place?
|
|
end
|
|
|
|
def forget_change(attr_name)
|
|
attributes[attr_name] = attributes[attr_name].forgetting_assignment
|
|
forced_changes.delete(attr_name)
|
|
end
|
|
|
|
def original_value(attr_name)
|
|
attributes[attr_name].original_value
|
|
end
|
|
|
|
def force_change(attr_name)
|
|
forced_changes[attr_name] = fetch_value(attr_name)
|
|
end
|
|
|
|
private
|
|
attr_reader :attributes
|
|
|
|
def forced_changes
|
|
@forced_changes ||= {}
|
|
end
|
|
|
|
def attr_names
|
|
attributes.keys
|
|
end
|
|
|
|
def attribute_changed?(attr_name)
|
|
forced_changes.include?(attr_name) || !!attributes[attr_name].changed?
|
|
end
|
|
|
|
def fetch_value(attr_name)
|
|
attributes.fetch_value(attr_name)
|
|
end
|
|
end
|
|
|
|
class ForcedMutationTracker < AttributeMutationTracker # :nodoc:
|
|
def initialize(attributes)
|
|
super
|
|
@finalized_changes = nil
|
|
end
|
|
|
|
def changed_in_place?(attr_name)
|
|
false
|
|
end
|
|
|
|
def change_to_attribute(attr_name)
|
|
if finalized_changes&.include?(attr_name)
|
|
finalized_changes[attr_name].dup
|
|
else
|
|
super
|
|
end
|
|
end
|
|
|
|
def forget_change(attr_name)
|
|
forced_changes.delete(attr_name)
|
|
end
|
|
|
|
def original_value(attr_name)
|
|
if changed?(attr_name)
|
|
forced_changes[attr_name]
|
|
else
|
|
fetch_value(attr_name)
|
|
end
|
|
end
|
|
|
|
def force_change(attr_name)
|
|
forced_changes[attr_name] = clone_value(attr_name) unless attribute_changed?(attr_name)
|
|
end
|
|
|
|
def finalize_changes
|
|
@finalized_changes = changes
|
|
end
|
|
|
|
private
|
|
attr_reader :finalized_changes
|
|
|
|
def attr_names
|
|
forced_changes.keys
|
|
end
|
|
|
|
def attribute_changed?(attr_name)
|
|
forced_changes.include?(attr_name)
|
|
end
|
|
|
|
def fetch_value(attr_name)
|
|
attributes.send(:_read_attribute, attr_name)
|
|
end
|
|
|
|
def clone_value(attr_name)
|
|
value = fetch_value(attr_name)
|
|
value.duplicable? ? value.clone : value
|
|
rescue TypeError, NoMethodError
|
|
value
|
|
end
|
|
end
|
|
|
|
class NullMutationTracker # :nodoc:
|
|
include Singleton
|
|
|
|
def changed_attribute_names
|
|
[]
|
|
end
|
|
|
|
def changed_values
|
|
{}
|
|
end
|
|
|
|
def changes
|
|
{}
|
|
end
|
|
|
|
def change_to_attribute(attr_name)
|
|
end
|
|
|
|
def any_changes?
|
|
false
|
|
end
|
|
|
|
def changed?(attr_name, **)
|
|
false
|
|
end
|
|
|
|
def changed_in_place?(attr_name)
|
|
false
|
|
end
|
|
|
|
def original_value(attr_name)
|
|
end
|
|
end
|
|
end
|