1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activemodel/lib/active_model/attribute_set/builder.rb
Ryuta Kamizono f93b04afab Deprecate marshalling load from legacy attributes format
Since #31827, marshalling attributes hash format is changed to improve
performance because materializing lazy attribute hash is too expensive.

In that time, we had kept an ability to load from legacy attributes
format, since that performance improvement is backported to 5-1-stable
and 5-0-stable.

Now all supported versions will dump attributes as new format, the
backward compatibity should no longer be needed.
2020-05-02 15:49:09 +09:00

124 lines
3.2 KiB
Ruby

# frozen_string_literal: true
require "active_model/attribute"
module ActiveModel
class AttributeSet # :nodoc:
class Builder # :nodoc:
attr_reader :types, :default_attributes
def initialize(types, default_attributes = {})
@types = types
@default_attributes = default_attributes
end
def build_from_database(values = {}, additional_types = {})
attributes = LazyAttributeHash.new(types, values, additional_types, default_attributes)
AttributeSet.new(attributes)
end
end
end
class LazyAttributeHash # :nodoc:
delegate :transform_values, :each_key, :each_value, :fetch, :except, to: :materialize
def initialize(types, values, additional_types, default_attributes, delegate_hash = {})
@types = types
@values = values
@additional_types = additional_types
@materialized = false
@delegate_hash = delegate_hash
@default_attributes = default_attributes
end
def key?(key)
delegate_hash.key?(key) || values.key?(key) || types.key?(key)
end
def [](key)
delegate_hash[key] || assign_default_value(key)
end
def []=(key, value)
delegate_hash[key] = value
end
def deep_dup
dup.tap do |copy|
copy.instance_variable_set(:@delegate_hash, delegate_hash.transform_values(&:dup))
end
end
def initialize_dup(_)
@delegate_hash = Hash[delegate_hash]
super
end
def select
keys = types.keys | values.keys | delegate_hash.keys
keys.each_with_object({}) do |key, hash|
attribute = self[key]
if yield(key, attribute)
hash[key] = attribute
end
end
end
def ==(other)
if other.is_a?(LazyAttributeHash)
materialize == other.materialize
else
materialize == other
end
end
def marshal_dump
[@types, @values, @additional_types, @default_attributes, @delegate_hash]
end
def marshal_load(values)
if values.is_a?(Hash)
ActiveSupport::Deprecation.warn(<<~MSG.squish)
Marshalling load from legacy attributes format is deprecated and will be removed in Rails 6.2.
MSG
empty_hash = {}.freeze
initialize(empty_hash, empty_hash, empty_hash, empty_hash, values)
@materialized = true
else
initialize(*values)
end
end
protected
def materialize
unless @materialized
values.each_key { |key| self[key] }
types.each_key { |key| self[key] }
unless frozen?
@materialized = true
end
end
delegate_hash
end
private
attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
def assign_default_value(name)
type = additional_types.fetch(name, types[name])
value_present = true
value = values.fetch(name) { value_present = false }
if value_present
delegate_hash[name] = Attribute.from_database(name, value, type)
elsif types.key?(name)
attr = default_attributes[name]
if attr
delegate_hash[name] = attr.dup
else
delegate_hash[name] = Attribute.uninitialized(name, type)
end
end
end
end
end