From 5bd96de6253bf45993a80ebc235b64f72b04d902 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 27 Jun 2012 23:06:35 -0300 Subject: Extract nested parameter assignment to a separate method --- activerecord/lib/active_record/attribute_assignment.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index a54c103476..bdd198791b 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -104,8 +104,7 @@ module ActiveRecord end end - # assign any deferred nested attributes after the base attributes have been set - nested_parameter_attributes.each { |k,v| _assign_attribute(k, v) } + assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty? assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty? ensure @mass_assignment_options = previous_options @@ -133,6 +132,11 @@ module ActiveRecord end end + # Assign any deferred nested attributes after the base attributes have been set. + def assign_nested_parameter_attributes(pairs) + pairs.each { |k, v| _assign_attribute(k, v) } + end + # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done # by calling new on the column type or aggregation type (through composed_of) object with these parameters. # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate @@ -252,6 +256,5 @@ module ActiveRecord def find_parameter_position(multiparameter_name) multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i end - end end -- cgit v1.2.3 From 2a700a03cee5366ad4a9af5b5f3a9c31a7e991fd Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 27 Jun 2012 23:22:40 -0300 Subject: Refactor some code in multiparameter assignment Move some methods to the top to better organize them, since they're used right at the beginning of the multiparameter assignment method chain. --- .../lib/active_record/attribute_assignment.rb | 51 +++++++++++----------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index bdd198791b..610411c0f0 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -150,14 +150,6 @@ module ActiveRecord ) end - def instantiate_time_object(name, values) - if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name)) - Time.zone.local(*values) - else - Time.time_with_datetime_fallback(self.class.default_timezone, *values) - end - end - def execute_callstack_for_multiparameter_attributes(callstack) errors = [] callstack.each do |name, values_with_empty_parameters| @@ -173,11 +165,33 @@ module ActiveRecord end end + def extract_callstack_for_multiparameter_attributes(pairs) + attributes = { } + + pairs.each do |(multiparameter_name, value)| + attribute_name = multiparameter_name.split("(").first + attributes[attribute_name] ||= {} + + parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value) + attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value + end + + attributes + end + + def instantiate_time_object(name, values) + if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name)) + Time.zone.local(*values) + else + Time.time_with_datetime_fallback(self.class.default_timezone, *values) + end + end + def read_value_from_parameter(name, values_hash_from_param) + return if values_hash_from_param.values.compact.empty? + klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass - if values_hash_from_param.values.all?{|v|v.nil?} - nil - elsif klass == Time + if klass == Time read_time_parameter_value(name, values_hash_from_param) elsif klass == Date read_date_parameter_value(name, values_hash_from_param) @@ -234,21 +248,6 @@ module ActiveRecord [values_hash_from_param.keys.max,upper_cap].min end - def extract_callstack_for_multiparameter_attributes(pairs) - attributes = { } - - pairs.each do |pair| - multiparameter_name, value = pair - attribute_name = multiparameter_name.split("(").first - attributes[attribute_name] = {} unless attributes.include?(attribute_name) - - parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value) - attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value - end - - attributes - end - def type_cast_attribute_value(multiparameter_name, value) multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value end -- cgit v1.2.3 From a0f9dc7657cca1bbee4957b8cff884dcb53a3d1b Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 27 Jun 2012 23:30:40 -0300 Subject: Reuse already fetched column to check for :time Avoid doing a new column lookup for the attribute, since we already have the column to check for the klass. --- activerecord/lib/active_record/attribute_assignment.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 610411c0f0..e174758fd9 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -190,9 +190,10 @@ module ActiveRecord def read_value_from_parameter(name, values_hash_from_param) return if values_hash_from_param.values.compact.empty? - klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass + column = self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name) + klass = column.klass if klass == Time - read_time_parameter_value(name, values_hash_from_param) + read_time_parameter_value(column, name, values_hash_from_param) elsif klass == Date read_date_parameter_value(name, values_hash_from_param) else @@ -200,12 +201,12 @@ module ActiveRecord end end - def read_time_parameter_value(name, values_hash_from_param) + def read_time_parameter_value(column, name, values_hash_from_param) # If column is a :time (and not :date or :timestamp) there is no need to validate if # there are year/month/day fields - if column_for_attribute(name).type == :time + if column.type == :time # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil - {1 => 1970, 2 => 1, 3 => 1}.each do |key,value| + { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value| values_hash_from_param[key] ||= value end else -- cgit v1.2.3 From 5021c13ceaccd7a1b80638bfc564470db22ea520 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 27 Jun 2012 23:40:16 -0300 Subject: Refactor missing parameter validation based on position --- .../lib/active_record/attribute_assignment.rb | 19 ++++++++++++------- activerecord/test/cases/attribute_methods_test.rb | 1 - 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index e174758fd9..b030e64562 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -211,9 +211,7 @@ module ActiveRecord end else # else column is a timestamp, so if Date bits were not provided, error - if missing_parameter = [1,2,3].detect{ |position| !values_hash_from_param.has_key?(position) } - raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter}i)") - end + validate_missing_parameters!(name, [1,2,3], values_hash_from_param) # If Date bits were provided but blank, then return nil return nil if (1..3).any? { |position| values_hash_from_param[position].blank? } @@ -238,13 +236,20 @@ module ActiveRecord def read_other_parameter_value(klass, name, values_hash_from_param) max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param) - values = (1..max_position).collect do |position| - raise "Missing Parameter" if !values_hash_from_param.has_key?(position) - values_hash_from_param[position] - end + positions = (1..max_position) + validate_missing_parameters!(name, positions, values_hash_from_param) + + values = values_hash_from_param.values_at(*positions) klass.new(*values) end + # If some position is not provided, it errors out a missing parameter exception. + def validate_missing_parameters!(name, positions, values_hash) + if missing_parameter = positions.detect { |position| !values_hash.key?(position) } + raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})") + end + end + def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100) [values_hash_from_param.keys.max,upper_cap].min end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 807971d678..4bc68acd13 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -34,7 +34,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert t.attribute_present?("written_on") assert !t.attribute_present?("content") assert !t.attribute_present?("author_name") - end def test_attribute_present_with_booleans -- cgit v1.2.3 From ef52d34d07b2925e1d785a65b6b9e19886b03e35 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 27 Jun 2012 23:43:58 -0300 Subject: Refactor blank date parameter validation --- activerecord/lib/active_record/attribute_assignment.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index b030e64562..cb8727b281 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -214,7 +214,7 @@ module ActiveRecord validate_missing_parameters!(name, [1,2,3], values_hash_from_param) # If Date bits were provided but blank, then return nil - return nil if (1..3).any? { |position| values_hash_from_param[position].blank? } + return if blank_date_parameter?(values_hash_from_param) end max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6) @@ -225,7 +225,7 @@ module ActiveRecord end def read_date_parameter_value(name, values_hash_from_param) - return nil if (1..3).any? {|position| values_hash_from_param[position].blank?} + return if blank_date_parameter?(values_hash_from_param) set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]] begin Date.new(*set_values) @@ -243,6 +243,10 @@ module ActiveRecord klass.new(*values) end + def blank_date_parameter?(values_hash) + (1..3).any? { |position| values_hash[position].blank? } + end + # If some position is not provided, it errors out a missing parameter exception. def validate_missing_parameters!(name, positions, values_hash) if missing_parameter = positions.detect { |position| !values_hash.key?(position) } -- cgit v1.2.3 From 022d7b38468f1cb94f783ac402fdd167f6e027fe Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 27 Jun 2012 23:55:24 -0300 Subject: Use cached column information to instantiate time object --- activerecord/lib/active_record/attribute_assignment.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index cb8727b281..8188057848 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -179,8 +179,8 @@ module ActiveRecord attributes end - def instantiate_time_object(name, values) - if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name)) + def instantiate_time_object(column, name, values) + if self.class.send(:create_time_zone_conversion_attribute?, name, column) Time.zone.local(*values) else Time.time_with_datetime_fallback(self.class.default_timezone, *values) @@ -195,7 +195,7 @@ module ActiveRecord if klass == Time read_time_parameter_value(column, name, values_hash_from_param) elsif klass == Date - read_date_parameter_value(name, values_hash_from_param) + read_date_parameter_value(column, name, values_hash_from_param) else read_other_parameter_value(klass, name, values_hash_from_param) end @@ -221,16 +221,16 @@ module ActiveRecord set_values = (1..max_position).collect{ |position| values_hash_from_param[position] } # If Time bits are not there, then default to 0 (3..5).each { |i| set_values[i] = set_values[i].blank? ? 0 : set_values[i] } - instantiate_time_object(name, set_values) + instantiate_time_object(column, name, set_values) end - def read_date_parameter_value(name, values_hash_from_param) + def read_date_parameter_value(column, name, values_hash_from_param) return if blank_date_parameter?(values_hash_from_param) set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]] begin Date.new(*set_values) rescue ArgumentError # if Date.new raises an exception on an invalid date - instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates + instantiate_time_object(column, name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates end end -- cgit v1.2.3 From 95f6b1245d2e45cba87642ac1884c3d6226ba164 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 28 Jun 2012 00:05:44 -0300 Subject: Some more cleanup to use Hash#values_at, and some method docs --- activerecord/lib/active_record/attribute_assignment.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 8188057848..f695c384cb 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -218,15 +218,15 @@ module ActiveRecord end max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6) - set_values = (1..max_position).collect{ |position| values_hash_from_param[position] } + set_values = values_hash_from_param.values_at(*(1..max_position)) # If Time bits are not there, then default to 0 - (3..5).each { |i| set_values[i] = set_values[i].blank? ? 0 : set_values[i] } + (3..5).each { |i| set_values[i] = set_values[i].presence || 0 } instantiate_time_object(column, name, set_values) end def read_date_parameter_value(column, name, values_hash_from_param) return if blank_date_parameter?(values_hash_from_param) - set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]] + set_values = values_hash_from_param.values_at(1,2,3) begin Date.new(*set_values) rescue ArgumentError # if Date.new raises an exception on an invalid date @@ -243,6 +243,10 @@ module ActiveRecord klass.new(*values) end + # Checks whether some blank date parameter exists. Note that this is different + # than the validate_missing_parameters! method, since it just checks for blank + # positions instead of missing ones, and does not raise in case one blank position + # exists. The caller is responsible to handle the case of this returning true. def blank_date_parameter?(values_hash) (1..3).any? { |position| values_hash[position].blank? } end -- cgit v1.2.3 From ec31680106003172b776b58d8c9c0a2f7ac3e4f2 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 28 Jun 2012 00:29:20 -0300 Subject: Move multiparameter attribute logic to a class This should make it easier to refactor and improve this code, and remove complexity with params going around here and there. --- .../lib/active_record/attribute_assignment.rb | 156 +++++++++++---------- 1 file changed, 84 insertions(+), 72 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index f695c384cb..28155ea803 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -154,7 +154,7 @@ module ActiveRecord errors = [] callstack.each do |name, values_with_empty_parameters| begin - send(name + "=", read_value_from_parameter(name, values_with_empty_parameters)) + send(name + "=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value) rescue => ex errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name) end @@ -179,95 +179,107 @@ module ActiveRecord attributes end - def instantiate_time_object(column, name, values) - if self.class.send(:create_time_zone_conversion_attribute?, name, column) - Time.zone.local(*values) - else - Time.time_with_datetime_fallback(self.class.default_timezone, *values) - end + def type_cast_attribute_value(multiparameter_name, value) + multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value + end + + def find_parameter_position(multiparameter_name) + multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i end - def read_value_from_parameter(name, values_hash_from_param) - return if values_hash_from_param.values.compact.empty? + class MultiparameterAttribute + attr_reader :object, :name, :values - column = self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name) - klass = column.klass - if klass == Time - read_time_parameter_value(column, name, values_hash_from_param) - elsif klass == Date - read_date_parameter_value(column, name, values_hash_from_param) - else - read_other_parameter_value(klass, name, values_hash_from_param) + def initialize(object, name, values) + @object = object + @name = name + @values = values end - end - def read_time_parameter_value(column, name, values_hash_from_param) - # If column is a :time (and not :date or :timestamp) there is no need to validate if - # there are year/month/day fields - if column.type == :time - # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil - { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value| - values_hash_from_param[key] ||= value - end - else - # else column is a timestamp, so if Date bits were not provided, error - validate_missing_parameters!(name, [1,2,3], values_hash_from_param) + def read_value + return if values.values.compact.empty? - # If Date bits were provided but blank, then return nil - return if blank_date_parameter?(values_hash_from_param) + column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name) + klass = column.klass + if klass == Time + read_time_parameter_value(column, name, values) + elsif klass == Date + read_date_parameter_value(column, name, values) + else + read_other_parameter_value(klass, name, values) + end end - max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6) - set_values = values_hash_from_param.values_at(*(1..max_position)) - # If Time bits are not there, then default to 0 - (3..5).each { |i| set_values[i] = set_values[i].presence || 0 } - instantiate_time_object(column, name, set_values) - end + private - def read_date_parameter_value(column, name, values_hash_from_param) - return if blank_date_parameter?(values_hash_from_param) - set_values = values_hash_from_param.values_at(1,2,3) - begin - Date.new(*set_values) - rescue ArgumentError # if Date.new raises an exception on an invalid date - instantiate_time_object(column, name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates + def instantiate_time_object(column, name, values) + if object.class.send(:create_time_zone_conversion_attribute?, name, column) + Time.zone.local(*values) + else + Time.time_with_datetime_fallback(object.class.default_timezone, *values) + end end - end - def read_other_parameter_value(klass, name, values_hash_from_param) - max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param) - positions = (1..max_position) - validate_missing_parameters!(name, positions, values_hash_from_param) + def read_time_parameter_value(column, name, values) + # If column is a :time (and not :date or :timestamp) there is no need to validate if + # there are year/month/day fields + if column.type == :time + # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil + { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value| + values[key] ||= value + end + else + # else column is a timestamp, so if Date bits were not provided, error + validate_missing_parameters!(name, [1,2,3], values) + + # If Date bits were provided but blank, then return nil + return if blank_date_parameter?(values) + end - values = values_hash_from_param.values_at(*positions) - klass.new(*values) - end + max_position = extract_max_param_for_multiparameter_attributes(values, 6) + set_values = values.values_at(*(1..max_position)) + # If Time bits are not there, then default to 0 + (3..5).each { |i| set_values[i] = set_values[i].presence || 0 } + instantiate_time_object(column, name, set_values) + end - # Checks whether some blank date parameter exists. Note that this is different - # than the validate_missing_parameters! method, since it just checks for blank - # positions instead of missing ones, and does not raise in case one blank position - # exists. The caller is responsible to handle the case of this returning true. - def blank_date_parameter?(values_hash) - (1..3).any? { |position| values_hash[position].blank? } - end + def read_date_parameter_value(column, name, values) + return if blank_date_parameter?(values) + set_values = values.values_at(1,2,3) + begin + Date.new(*set_values) + rescue ArgumentError # if Date.new raises an exception on an invalid date + instantiate_time_object(column, name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates + end + end - # If some position is not provided, it errors out a missing parameter exception. - def validate_missing_parameters!(name, positions, values_hash) - if missing_parameter = positions.detect { |position| !values_hash.key?(position) } - raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})") + def read_other_parameter_value(klass, name, values) + max_position = extract_max_param_for_multiparameter_attributes(values) + positions = (1..max_position) + validate_missing_parameters!(name, positions, values) + + values = values.values_at(*positions) + klass.new(*values) end - end - def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100) - [values_hash_from_param.keys.max,upper_cap].min - end + # Checks whether some blank date parameter exists. Note that this is different + # than the validate_missing_parameters! method, since it just checks for blank + # positions instead of missing ones, and does not raise in case one blank position + # exists. The caller is responsible to handle the case of this returning true. + def blank_date_parameter?(values_hash) + (1..3).any? { |position| values_hash[position].blank? } + end - def type_cast_attribute_value(multiparameter_name, value) - multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value - end + # If some position is not provided, it errors out a missing parameter exception. + def validate_missing_parameters!(name, positions, values_hash) + if missing_parameter = positions.detect { |position| !values_hash.key?(position) } + raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})") + end + end - def find_parameter_position(multiparameter_name) - multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i + def extract_max_param_for_multiparameter_attributes(values, upper_cap = 100) + [values.keys.max,upper_cap].min + end end end end -- cgit v1.2.3 From ec01242a21d601326af7578fd5e1d32dbe43e9d2 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 28 Jun 2012 00:36:25 -0300 Subject: Get rid of some arguments by using the accessors Cleans up a lot of noise from arguments being passed from one method to another. --- .../lib/active_record/attribute_assignment.rb | 61 +++++++++++----------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 28155ea803..d9989274c8 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -154,7 +154,7 @@ module ActiveRecord errors = [] callstack.each do |name, values_with_empty_parameters| begin - send(name + "=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value) + send("#{name}=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value) rescue => ex errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name) end @@ -187,8 +187,8 @@ module ActiveRecord multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i end - class MultiparameterAttribute - attr_reader :object, :name, :values + class MultiparameterAttribute #:nodoc: + attr_reader :object, :name, :values, :column def initialize(object, name, values) @object = object @@ -199,28 +199,29 @@ module ActiveRecord def read_value return if values.values.compact.empty? - column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name) - klass = column.klass + @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name) + klass = column.klass + if klass == Time - read_time_parameter_value(column, name, values) + read_time elsif klass == Date - read_date_parameter_value(column, name, values) + read_date else - read_other_parameter_value(klass, name, values) + read_other(klass) end end private - def instantiate_time_object(column, name, values) + def instantiate_time_object(set_values) if object.class.send(:create_time_zone_conversion_attribute?, name, column) - Time.zone.local(*values) + Time.zone.local(*set_values) else - Time.time_with_datetime_fallback(object.class.default_timezone, *values) + Time.time_with_datetime_fallback(object.class.default_timezone, *set_values) end end - def read_time_parameter_value(column, name, values) + def read_time # If column is a :time (and not :date or :timestamp) there is no need to validate if # there are year/month/day fields if column.type == :time @@ -230,55 +231,55 @@ module ActiveRecord end else # else column is a timestamp, so if Date bits were not provided, error - validate_missing_parameters!(name, [1,2,3], values) + validate_missing_parameters!([1,2,3]) # If Date bits were provided but blank, then return nil - return if blank_date_parameter?(values) + return if blank_date_parameter? end - max_position = extract_max_param_for_multiparameter_attributes(values, 6) + max_position = extract_max_param(6) set_values = values.values_at(*(1..max_position)) # If Time bits are not there, then default to 0 (3..5).each { |i| set_values[i] = set_values[i].presence || 0 } - instantiate_time_object(column, name, set_values) + instantiate_time_object(set_values) end - def read_date_parameter_value(column, name, values) - return if blank_date_parameter?(values) + def read_date + return if blank_date_parameter? set_values = values.values_at(1,2,3) begin Date.new(*set_values) rescue ArgumentError # if Date.new raises an exception on an invalid date - instantiate_time_object(column, name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates + instantiate_time_object(set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates end end - def read_other_parameter_value(klass, name, values) - max_position = extract_max_param_for_multiparameter_attributes(values) + def read_other(klass) + max_position = extract_max_param positions = (1..max_position) - validate_missing_parameters!(name, positions, values) + validate_missing_parameters!(positions) - values = values.values_at(*positions) - klass.new(*values) + set_values = values.values_at(*positions) + klass.new(*set_values) end # Checks whether some blank date parameter exists. Note that this is different # than the validate_missing_parameters! method, since it just checks for blank # positions instead of missing ones, and does not raise in case one blank position # exists. The caller is responsible to handle the case of this returning true. - def blank_date_parameter?(values_hash) - (1..3).any? { |position| values_hash[position].blank? } + def blank_date_parameter? + (1..3).any? { |position| values[position].blank? } end # If some position is not provided, it errors out a missing parameter exception. - def validate_missing_parameters!(name, positions, values_hash) - if missing_parameter = positions.detect { |position| !values_hash.key?(position) } + def validate_missing_parameters!(positions) + if missing_parameter = positions.detect { |position| !values.key?(position) } raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})") end end - def extract_max_param_for_multiparameter_attributes(values, upper_cap = 100) - [values.keys.max,upper_cap].min + def extract_max_param(upper_cap = 100) + [values.keys.max, upper_cap].min end end end -- cgit v1.2.3