From 3bf364a395db1387c42b79c68fb4f73a10a04460 Mon Sep 17 00:00:00 2001 From: Adam Meehan Date: Sat, 4 Dec 2010 16:29:05 +1100 Subject: [PATCH] reluctantly overriding whole execute_callstack_for_multiparameter_attributes method this fixes issue for Date column types. Damn method is just too unwieldly and should be refactored in Rails. --- .../extensions/multiparameter_handler.rb | 62 +++++++++++++++---- .../extensions/multiparameter_handler_spec.rb | 32 +++++++--- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/lib/validates_timeliness/extensions/multiparameter_handler.rb b/lib/validates_timeliness/extensions/multiparameter_handler.rb index daf0037..ddbe687 100644 --- a/lib/validates_timeliness/extensions/multiparameter_handler.rb +++ b/lib/validates_timeliness/extensions/multiparameter_handler.rb @@ -3,26 +3,64 @@ module ValidatesTimeliness module MultiparameterHandler extend ActiveSupport::Concern + # Stricter handling of date and time values from multiparameter + # assignment from the date/time select view helpers + included do alias_method_chain :instantiate_time_object, :timeliness + alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness end private - # Stricter handling of date and time values from multiparameter - # assignment from the date/time select view helpers - # - def instantiate_time_object_with_timeliness(name, values) - unless Date.valid_civil?(*values[0..2]) - value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-") - value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty? - return value - end + def invalid_multiparameter_date_or_time_as_string(values) + value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-") + value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty? + value + end - if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name)) - Time.zone.local(*values) + def instantiate_time_object_with_timeliness(name, values) + if Date.valid_civil?(*values[0..2]) + instantiate_time_object_without_timeliness(name, values) else - Time.time_with_datetime_fallback(self.class.default_timezone, *values) + invalid_multiparameter_date_or_time_as_string(values) + end + end + + def instantiate_date_object(name, values) + values = values.map { |v| v.nil? ? 1 : v } + Date.new(*values) + rescue ArgumentError => ex + invalid_multiparameter_date_or_time_as_string(values) + end + + def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack) + errors = [] + callstack.each do |name, values_with_empty_parameters| + begin + klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass + values = values_with_empty_parameters.reject { |v| v.nil? } + + if values.empty? + send(name + "=", nil) + else + + value = if Time == klass + instantiate_time_object(name, values) + elsif Date == klass + instantiate_date_object(name, values_with_empty_parameters) + else + klass.new(*values) + end + + send(name + "=", value) + end + rescue => ex + errors << ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name) + end + end + unless errors.empty? + raise ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes" end end diff --git a/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb b/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb index ede2c6a..c5ec057 100644 --- a/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb +++ b/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb @@ -3,19 +3,31 @@ require 'spec_helper' describe ValidatesTimeliness::Extensions::MultiparameterHandler do let(:employee) { Employee.new } - it 'should return string value for invalid dates' do - instantiate_time_object('birth_date', [2000, 2, 31]).should == '2000-02-31' + context "time column" do + it 'should return string value for invalid date portion' do + multiparameter_attribute(:birth_datetime, [2000, 2, 31, 12, 0, 0]) + employee.birth_datetime_before_type_cast.should == '2000-02-31 12:00:00' + end + + it 'should return Time value for valid datetimes' do + multiparameter_attribute(:birth_datetime, [2000, 2, 28, 12, 0, 0]) + employee.birth_datetime_before_type_cast.should be_kind_of(Time) + end end - it 'should return string value for invalid datetimes' do - instantiate_time_object('birth_datetime', [2000, 2, 31, 12, 0, 0]).should == '2000-02-31 12:00:00' - end - - it 'should return Time value for valid datetimes' do - instantiate_time_object('birth_datetime', [2000, 2, 28, 12, 0, 0]).should be_kind_of(Time) + context "date column" do + it 'should return string value for invalid date' do + multiparameter_attribute(:birth_date, [2000, 2, 31]) + employee.birth_date_before_type_cast.should == '2000-02-31' + end + + it 'should return Date value for valid date' do + multiparameter_attribute(:birth_date, [2000, 2, 28]) + employee.birth_date_before_type_cast.should be_kind_of(Date) + end end - def instantiate_time_object(name, values) - employee.send(:instantiate_time_object, name, values) + def multiparameter_attribute(name, values) + employee.send(:execute_callstack_for_multiparameter_attributes, name.to_s => values) end end