diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 78b3507..0eb5bd3 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1734,19 +1734,22 @@ MSG callstack.each do |name, values_with_empty_parameters| begin klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass - # in order to allow a date to be set without a year, we must keep the empty values. - # Otherwise, we wouldn't be able to distinguish it from a date with an empty day. - values = values_with_empty_parameters.reject { |v| v.nil? } + values = values_with_empty_parameters.map { |v| v.last } - if values.empty? + unless values.any? { |v| !v.nil? } send(name + "=", nil) else value = if Time == klass - instantiate_time_object(name, values) + if values_with_empty_parameters.map(&:first) & [1, 2, 3] != [1, 2, 3] + errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", nil, name) + else + (0..2).each { |i| values[i] = 1 if values[i].nil? } + instantiate_time_object(name, values) + end elsif Date == klass begin - values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end + values = values.collect do |v| v.nil? ? 1 : v end Date.new(*values) rescue ArgumentError => ex # if Date.new raises an exception on an invalid date instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates @@ -1778,7 +1781,7 @@ MSG attributes[attribute_name] << [ find_parameter_position(multiparameter_name), parameter_value ] end - attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } } + attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first } } end def type_cast_attribute_value(multiparameter_name, value) @@ -1786,7 +1789,7 @@ MSG end def find_parameter_position(multiparameter_name) - multiparameter_name.scan(/\(([0-9]*).*\)/).first.first + multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i end # Returns a comma-separated pair list, like "key1 = val1, key2 = val2". diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 16fd9a7..156c30d 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -598,6 +598,46 @@ class BasicsTest < ActiveRecord::TestCase assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on end + def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_parts + attributes = { + "written_on(4i)" => "16", "written_on(5i)" => "24" + } + topic = Topic.find(1) + assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + topic.attributes = attributes + end + end + + def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_parts + attributes = { + "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02" + } + topic = Topic.find(1) + assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + topic.attributes = attributes + end + end + + def test_multiparameter_attributes_on_time_will_ignore_date_if_empty + attributes = { + "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", + "written_on(4i)" => "16", "written_on(5i)" => "24" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(1, 1, 1, 16, 24), topic.written_on + end + + def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty + attributes = { + "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "", + "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(1, 1, 1, 16, 12, 02), topic.written_on + end + def test_multiparameter_assignment_of_aggregation customer = Customer.new address = Address.new("The Street", "The City", "The Country")