From 5bd96de6253bf45993a80ebc235b64f72b04d902 Mon Sep 17 00:00:00 2001
From: Carlos Antonio da Silva <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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 <carlosantoniodasilva@gmail.com>
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(-)

(limited to 'activerecord')

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