diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb index d057f0941a..9ee9a7815f 100644 --- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb +++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb @@ -43,9 +43,7 @@ module ActiveRecord # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21" # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21" def read_attribute_before_type_cast(attr_name) - if attr = @attributes[attr_name.to_s] - attr.value_before_type_cast - end + @attributes[attr_name.to_s].value_before_type_cast end # Returns a hash of attributes before typecasting and deserialization. diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index ca71834641..e1a86fd3aa 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -129,7 +129,7 @@ module ActiveRecord end def _field_changed?(attr, old_value) - attribute_named(attr).changed_from?(old_value) + @attributes[attr].changed_from?(old_value) end def changed_in_place @@ -140,7 +140,7 @@ module ActiveRecord def changed_in_place?(attr_name) old_value = original_raw_attribute(attr_name) - attribute_named(attr_name).changed_in_place_from?(old_value) + @attributes[attr_name].changed_in_place_from?(old_value) end def original_raw_attribute(attr_name) @@ -154,7 +154,7 @@ module ActiveRecord end def store_original_raw_attribute(attr_name) - original_raw_attributes[attr_name] = attribute_named(attr_name).value_for_database + original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database end def store_original_raw_attributes diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 525c46970a..8c1cc128f7 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -99,10 +99,6 @@ module ActiveRecord def attribute(attribute_name) read_attribute(attribute_name) end - - def attribute_named(attribute_name) - @attributes.fetch(attribute_name, Attribute::Null) - end end end end diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb index 39f4ffe628..8f6b73eac2 100644 --- a/activerecord/lib/active_record/attribute_set.rb +++ b/activerecord/lib/active_record/attribute_set.rb @@ -40,7 +40,8 @@ module ActiveRecord end def build_from_database(values, additional_types = {}) - attributes = values.each_with_object({}) do |(name, value), hash| + attributes = Hash.new(Attribute::Null) + values.each_with_object(attributes) do |(name, value), hash| type = additional_types.fetch(name, @types[name]) hash[name] = Attribute.from_database(value, type) end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 3c24dc6420..f444a32d74 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -523,7 +523,9 @@ module ActiveRecord def init_internals pk = self.class.primary_key - @attributes[pk] ||= Attribute.from_database(nil, type_for_attribute(pk)) + unless @attributes.include?(pk) + @attributes[pk] = Attribute.from_database(nil, type_for_attribute(pk)) + end @aggregation_cache = {} @association_cache = {} diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index e63d278650..402a611efa 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -18,6 +18,14 @@ module ActiveRecord assert_equal 4, attributes[:bar].value end + test "[] returns a null object" do + builder = AttributeSet::Builder.new(foo: Type::Float.new) + attributes = builder.build_from_database(foo: '3.3') + + assert_equal '3.3', attributes[:foo].value_before_type_cast + assert_equal nil, attributes[:bar].value_before_type_cast + end + test "duping creates a new hash and dups each attribute" do builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) attributes = builder.build_from_database(foo: 1, bar: 'foo')