From ff74c64b6e45d20dfa21dd7c356cd44d33671465 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 9 Aug 2009 09:20:18 +0100 Subject: [PATCH] With multiparameter date attributes, the behaviour when empty fields are present is now coherent with the one described in the date_select documentation. --- activerecord/lib/active_record/base.rb | 14 ++++++--- activerecord/test/cases/base_test.rb | 47 +++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d155837..4bb48ee 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3028,16 +3028,22 @@ module ActiveRecord #:nodoc: def execute_callstack_for_multiparameter_attributes(callstack) errors = [] - callstack.each do |name, values| + 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(&:nil?) + if values.empty? send(name + "=", nil) else + value = if Time == klass instantiate_time_object(name, values) elsif Date == klass begin + values = values_with_empty_parameters.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 @@ -3065,10 +3071,8 @@ module ActiveRecord #:nodoc: attribute_name = multiparameter_name.split("(").first attributes[attribute_name] = [] unless attributes.include?(attribute_name) - unless value.empty? - attributes[attribute_name] << - [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ] - end + parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value) + 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 } } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index e57e46f..4530eec 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1012,7 +1012,25 @@ class BasicsTest < ActiveRecord::TestCase assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date end - def test_multiparameter_attributes_on_date_with_empty_date + def test_multiparameter_attributes_on_date_with_empty_year + attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(1, 6, 24), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_month + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(2004, 1, 24), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_day attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" } topic = Topic.find(1) topic.attributes = attributes @@ -1021,6 +1039,33 @@ class BasicsTest < ActiveRecord::TestCase assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date end + def test_multiparameter_attributes_on_date_with_empty_day_and_year + attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(1, 6, 1), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_day_and_month + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(2004, 1, 1), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_year_and_month + attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(1, 1, 24), topic.last_read.to_date + end + def test_multiparameter_attributes_on_date_with_all_empty attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" } topic = Topic.find(1) -- 1.6.3.3