From ff4986b917f6cc64398e2ba687f998fc52cf630c Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 31 May 2016 09:56:19 -0400 Subject: Ensure hashes can be passed to attributes using `composed_of` This behavior was broken by 36e9be85. When the value is assigned directly, either through mass assignment or directly assigning a hash, the hash gets passed through to this writer method directly. While this is intended to handle certain cases, when an explicit converter has been provided, we should continue to use that instead. The positioning of the added guard caused the new behavior to override that case. Fixes #25210 --- activerecord/CHANGELOG.md | 5 +++++ activerecord/lib/active_record/aggregations.rb | 9 +++++---- activerecord/test/cases/aggregations_test.rb | 5 +++++ activerecord/test/models/customer.rb | 7 ++++++- 4 files changed, 21 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 929f241b1b..a1a4d2646f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Ensure hashes can be assigned to attributes created using `composed_of`. + Fixes #25210. + + *Sean Griffin* + * Fix logging edge case where if an attribute was of the binary type and was provided as a Hash. diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 3ff41ed81b..8bed5bca28 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -256,15 +256,16 @@ module ActiveRecord def writer_method(name, class_name, mapping, allow_nil, converter) define_method("#{name}=") do |part| klass = class_name.constantize - if part.is_a?(Hash) - raise ArgumentError unless part.size == part.keys.max - part = klass.new(*part.sort.map(&:last)) - end unless part.is_a?(klass) || converter.nil? || part.nil? part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part) end + if part.is_a?(Hash) + raise ArgumentError unless part.size == part.keys.max + part = klass.new(*part.sort.map(&:last)) + end + if part.nil? && allow_nil mapping.each { |key, _| self[key] = nil } @aggregation_cache[name] = nil diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb index 5536702f58..8a728902a8 100644 --- a/activerecord/test/cases/aggregations_test.rb +++ b/activerecord/test/cases/aggregations_test.rb @@ -138,6 +138,11 @@ class AggregationsTest < ActiveRecord::TestCase assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s assert_kind_of Fullname, customers(:barney).fullname end + + def test_assigning_hash_to_custom_converter + customers(:barney).fullname = { first: "Barney", last: "Stinson" } + assert_equal "Barney STINSON", customers(:barney).name + end end class OverridingAggregationsTest < ActiveRecord::TestCase diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb index afe4b3d707..3338aaf7e1 100644 --- a/activerecord/test/models/customer.rb +++ b/activerecord/test/models/customer.rb @@ -64,7 +64,12 @@ class Fullname def self.parse(str) return nil unless str - new(*str.to_s.split) + + if str.is_a?(Hash) + new(str[:first], str[:last]) + else + new(*str.to_s.split) + end end def initialize(first, last = nil) -- cgit v1.2.3