aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
authorSean Griffin <sean@seantheprogrammer.com>2017-11-27 14:06:51 -0700
committerSean Griffin <sean@seantheprogrammer.com>2017-11-27 14:06:51 -0700
commit95b86e57a6c07bc636f8feab4afd4c7fe0ca512c (patch)
treec665e7cd5c61f2c6cd2dae0374838e8c144c98b5 /activemodel
parent924a368f5c654f5304e575c767eb0fc64adc8659 (diff)
downloadrails-95b86e57a6c07bc636f8feab4afd4c7fe0ca512c.tar.gz
rails-95b86e57a6c07bc636f8feab4afd4c7fe0ca512c.tar.bz2
rails-95b86e57a6c07bc636f8feab4afd4c7fe0ca512c.zip
Change how `AttributeSet::Builder` receives its defaults
There are two concerns which are both being combined into one here, but both have the same goal. There are certain attributes which we want to always consider initialized. Previously, they were handled separately. The primary key (which is assumed to be backed by a database column) needs to be initialized, because there is a ton of code in Active Record that assumes `foo.id` will never raise. Additionally, we want attributes which aren't backed by a database column to always be initialized, since we would never receive a database value for them. Ultimately these two concerns can be combined into one. The old implementation hid a lot of inherent complexity, and is hard to optimize from the outside. We can simplify things significantly by just passing in a hash. This has slightly different semantics from the old behavior, in that `Foo.select(:bar).first.id` will return the default value for the primary key, rather than `nil` unconditionally -- however, the default value is always `nil` in practice.
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/lib/active_model/attribute_set.rb2
-rw-r--r--activemodel/lib/active_model/attribute_set/builder.rb28
-rw-r--r--activemodel/test/cases/attribute_set_test.rb3
3 files changed, 17 insertions, 16 deletions
diff --git a/activemodel/lib/active_model/attribute_set.rb b/activemodel/lib/active_model/attribute_set.rb
index a892accbc6..54a5dd4064 100644
--- a/activemodel/lib/active_model/attribute_set.rb
+++ b/activemodel/lib/active_model/attribute_set.rb
@@ -5,7 +5,7 @@ require "active_model/attribute_set/yaml_encoder"
module ActiveModel
class AttributeSet # :nodoc:
- delegate :each_value, :fetch, to: :attributes
+ delegate :each_value, :fetch, :except, to: :attributes
def initialize(attributes)
@attributes = attributes
diff --git a/activemodel/lib/active_model/attribute_set/builder.rb b/activemodel/lib/active_model/attribute_set/builder.rb
index f94f47370f..758eb830fc 100644
--- a/activemodel/lib/active_model/attribute_set/builder.rb
+++ b/activemodel/lib/active_model/attribute_set/builder.rb
@@ -5,35 +5,30 @@ require "active_model/attribute"
module ActiveModel
class AttributeSet # :nodoc:
class Builder # :nodoc:
- attr_reader :types, :always_initialized, :default
+ attr_reader :types, :default_attributes
- def initialize(types, always_initialized = nil, &default)
+ def initialize(types, default_attributes = {})
@types = types
- @always_initialized = always_initialized
- @default = default
+ @default_attributes = default_attributes
end
def build_from_database(values = {}, additional_types = {})
- if always_initialized && !values.key?(always_initialized)
- values[always_initialized] = nil
- end
-
- attributes = LazyAttributeHash.new(types, values, additional_types, &default)
+ 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, to: :materialize
+ delegate :transform_values, :each_key, :each_value, :fetch, :except, to: :materialize
- def initialize(types, values, additional_types, &default)
+ def initialize(types, values, additional_types, default_attributes)
@types = types
@values = values
@additional_types = additional_types
@materialized = false
@delegate_hash = {}
- @default = default || proc {}
+ @default_attributes = default_attributes
end
def key?(key)
@@ -94,7 +89,7 @@ module ActiveModel
protected
- attr_reader :types, :values, :additional_types, :delegate_hash, :default
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
def materialize
unless @materialized
@@ -117,7 +112,12 @@ module ActiveModel
if value_present
delegate_hash[name] = Attribute.from_database(name, value, type)
elsif types.key?(name)
- delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type)
+ attr = default_attributes[name]
+ if attr
+ delegate_hash[name] = attr.dup
+ else
+ delegate_hash[name] = Attribute.uninitialized(name, type)
+ end
end
end
end
diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb
index 02c44c5d45..6e522d6c80 100644
--- a/activemodel/test/cases/attribute_set_test.rb
+++ b/activemodel/test/cases/attribute_set_test.rb
@@ -163,7 +163,8 @@ module ActiveModel
end
test "the primary_key is always initialized" do
- builder = AttributeSet::Builder.new({ foo: Type::Integer.new }, :foo)
+ defaults = { foo: Attribute.from_user(:foo, nil, nil) }
+ builder = AttributeSet::Builder.new({ foo: Type::Integer.new }, defaults)
attributes = builder.build_from_database
assert attributes.key?(:foo)