diff options
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/CHANGELOG.md | 11 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/before_type_cast.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/dirty.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/read.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_set.rb | 8 | ||||
-rw-r--r-- | activerecord/lib/active_record/core.rb | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/persistence.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/querying.rb | 9 | ||||
-rw-r--r-- | activerecord/lib/active_record/store.rb | 2 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_methods_test.rb | 31 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_set_test.rb | 16 | ||||
-rw-r--r-- | activerecord/test/cases/persistence_test.rb | 7 |
13 files changed, 81 insertions, 31 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7305c2c738..c87565452d 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,14 @@ +* `reload` no longer merges with the existing attributes. + The attribute hash is fully replaced. The record is put into the same state + as it would be with `Model.find(model.id)`. + + *Sean Griffin* + +* The object returned from `select_all` must respond to `column_types`. + If this is not the case a `NoMethodError` is raised. + + *Sean Griffin* + * `has_many :through` associations will no longer save the through record twice when added in an `after_create` callback defined before the associations. diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 268cec6160..267377cec6 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -271,9 +271,7 @@ module ActiveRecord # person.attributes # # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22} def attributes - attribute_names.each_with_object({}) { |name, attrs| - attrs[name] = read_attribute(name) - } + @attributes.to_hash end # Returns an <tt>#inspect</tt>-like string for the value of the 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 102ef17e16..5e5f7ca1b5 100644 --- a/activerecord/lib/active_record/attribute_set.rb +++ b/activerecord/lib/active_record/attribute_set.rb @@ -6,9 +6,10 @@ module ActiveRecord @attributes = attributes end - def update(other) - attributes.update(other.attributes) + def to_hash + attributes.each_with_object({}) { |(k, v), h| h[k] = v.value } end + alias_method :to_h, :to_hash def freeze @attributes.freeze @@ -35,7 +36,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..10b9e27b7d 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -254,7 +254,6 @@ module ActiveRecord end @attributes = self.class.attributes_builder.build_from_database(defaults) - @column_types = self.class.column_types init_internals initialize_internals_callback @@ -280,7 +279,6 @@ module ActiveRecord # post.title # => 'hello world' def init_with(coder) @attributes = coder['attributes'] - @column_types = self.class.column_types init_internals @@ -523,7 +521,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/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 6707f12489..ee634d7bb3 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -395,9 +395,7 @@ module ActiveRecord self.class.unscoped { self.class.find(id) } end - @attributes.update(fresh_object.instance_variable_get('@attributes')) - - @column_types = self.class.column_types + @attributes = fresh_object.instance_variable_get('@attributes') self end diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index 39817703cd..a9ddd9141f 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -37,14 +37,7 @@ module ActiveRecord # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }] def find_by_sql(sql, binds = []) result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds) - column_types = {} - - if result_set.respond_to? :column_types - column_types = result_set.column_types.except(*columns_hash.keys) - else - ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`" - end - + column_types = result_set.column_types.except(*columns_hash.keys) result_set.map { |record| instantiate(record, column_types) } end diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 7014bc6d45..219e19dbb8 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -129,7 +129,7 @@ module ActiveRecord private def store_accessor_for(store_attribute) - @column_types[store_attribute.to_s].accessor + type_for_attribute(store_attribute.to_s).accessor end class HashAccessor diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index f832ca3451..7566af920f 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -831,6 +831,37 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end + def test_attribute_method? + assert @target.attribute_method?(:title) + assert @target.attribute_method?(:title=) + assert_not @target.attribute_method?(:wibble) + end + + def test_attribute_method_returns_false_if_table_does_not_exist + @target.table_name = 'wibble' + assert_not @target.attribute_method?(:title) + end + + def test_attribute_names_on_new_record + model = @target.new + + assert_equal @target.column_names, model.attribute_names + end + + def test_attribute_names_on_queried_record + model = @target.last! + + assert_equal @target.column_names, model.attribute_names + end + + def test_attribute_names_with_custom_select + model = @target.select('id').last! + + assert_equal ['id'], model.attribute_names + # Sanity check, make sure other columns exist + assert_not_equal ['id'], @target.column_names + end + private def new_topic_like_ar_class(&block) diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index 091f7e396a..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') @@ -45,5 +53,13 @@ module ActiveRecord assert clone.frozen? assert_not attributes.frozen? end + + test "to_hash returns a hash of the type cast values" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new) + attributes = builder.build_from_database(foo: '1.1', bar: '2.2') + + assert_equal({ foo: 1, bar: 2.2 }, attributes.to_hash) + assert_equal({ foo: 1, bar: 2.2 }, attributes.to_h) + end end end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 28341d0b42..1192ecd6b4 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -858,4 +858,11 @@ class PersistenceTest < ActiveRecord::TestCase post.body end end + + def test_reload_removes_custom_selects + post = Post.select('posts.*, 1 as wibble').last! + + assert_equal 1, post[:wibble] + assert_nil post.reload[:wibble] + end end |