From cdc112e3ea8bd7b5ba787e64f3f8ee3da3e5a64f Mon Sep 17 00:00:00 2001
From: Matthew Draper <matthew@trebex.net>
Date: Thu, 4 Feb 2016 08:02:45 +1030
Subject: Defer Arel attribute lookup to the model class

This still isn't as separated as I'd like, but it at least moves most of
the burden of alias mapping in one place.
---
 .../associations/join_dependency/join_association.rb         |  2 +-
 .../lib/active_record/associations/preloader/association.rb  |  2 +-
 activerecord/lib/active_record/core.rb                       |  5 +++++
 activerecord/lib/active_record/inheritance.rb                |  2 +-
 activerecord/lib/active_record/relation.rb                   |  8 ++++----
 activerecord/lib/active_record/relation/batches.rb           |  6 +++---
 activerecord/lib/active_record/relation/calculations.rb      | 12 ++----------
 activerecord/lib/active_record/relation/finder_methods.rb    |  4 ++--
 .../relation/predicate_builder/relation_handler.rb           |  2 +-
 activerecord/lib/active_record/relation/query_methods.rb     | 12 +++++-------
 activerecord/lib/active_record/table_metadata.rb             |  6 +++++-
 activerecord/test/cases/relation/mutation_test.rb            |  4 ++++
 12 files changed, 34 insertions(+), 31 deletions(-)

diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index be65cf318c..708b3af5bd 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -75,7 +75,7 @@ module ActiveRecord
               column = klass.columns_hash[reflection.type.to_s]
 
               binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
-              constraint = constraint.and table[reflection.type].eq(Arel::Nodes::BindParam.new)
+              constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
             end
 
             joins << table.create_join(table, table.create_on(constraint), join_type)
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index e11a5cfb8a..3032bc786e 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -47,7 +47,7 @@ module ActiveRecord
         # This is overridden by HABTM as the condition should be on the foreign_key column in
         # the join table
         def association_key
-          table[association_key_name]
+          klass.arel_attribute(association_key_name, table)
         end
 
         # The name of the key on the model which declares the association
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 475a298467..aa8f22ce42 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -256,6 +256,11 @@ module ActiveRecord
           end
       end
 
+      def arel_attribute(name, table) # :nodoc:
+        name = attribute_alias(name) if attribute_alias?(name)
+        table[name]
+      end
+
       def predicate_builder # :nodoc:
         @predicate_builder ||= PredicateBuilder.new(table_metadata)
       end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 3b6fb70d0d..899683ee4f 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -192,7 +192,7 @@ module ActiveRecord
       end
 
       def type_condition(table = arel_table)
-        sti_column = table[inheritance_column]
+        sti_column = arel_attribute(inheritance_column, table)
         sti_names  = ([self] + descendants).map(&:sti_name)
 
         sti_column.in(sti_names)
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 032b8d4c5d..baddf4828a 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -47,7 +47,7 @@ module ActiveRecord
 
         if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
           primary_key_value = connection.next_sequence_value(klass.sequence_name)
-          values[klass.arel_table[klass.primary_key]] = primary_key_value
+          values[klass.arel_attribute(klass.primary_key, table)] = primary_key_value
         end
       end
 
@@ -373,9 +373,9 @@ module ActiveRecord
       stmt.table(table)
 
       if joins_values.any?
-        @klass.connection.join_to_update(stmt, arel, table[primary_key])
+        @klass.connection.join_to_update(stmt, arel, @klass.arel_attribute(primary_key, table))
       else
-        stmt.key = table[primary_key]
+        stmt.key = @klass.arel_attribute(primary_key, table)
         stmt.take(arel.limit)
         stmt.order(*arel.orders)
         stmt.wheres = arel.constraints
@@ -527,7 +527,7 @@ module ActiveRecord
         stmt.from(table)
 
         if joins_values.any?
-          @klass.connection.join_to_delete(stmt, arel, table[primary_key])
+          @klass.connection.join_to_delete(stmt, arel, @klass.arel_attribute(primary_key, table))
         else
           stmt.wheres = arel.constraints
         end
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 54587ae18e..af951d4da7 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -204,15 +204,15 @@ module ActiveRecord
         yield yielded_relation
 
         break if ids.length < of
-        batch_relation = relation.where(table[primary_key].gt(primary_key_offset))
+        batch_relation = relation.where(klass.arel_attribute(primary_key, table).gt(primary_key_offset))
       end
     end
 
     private
 
     def apply_limits(relation, start, finish)
-      relation = relation.where(table[primary_key].gteq(start)) if start
-      relation = relation.where(table[primary_key].lteq(finish)) if finish
+      relation = relation.where(klass.arel_attribute(primary_key, table).gteq(start)) if start
+      relation = relation.where(klass.arel_attribute(primary_key, table).lteq(finish)) if finish
       relation
     end
 
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index f45844a9ea..c02b648cb1 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -155,15 +155,7 @@ module ActiveRecord
     # See also #ids.
     #
     def pluck(*column_names)
-      column_names.map! do |column_name|
-        if column_name.is_a?(Symbol) && attribute_alias?(column_name)
-          attribute_alias(column_name)
-        else
-          column_name.to_s
-        end
-      end
-
-      if loaded? && (column_names - @klass.column_names).empty?
+      if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
         return @records.pluck(*column_names)
       end
 
@@ -172,7 +164,7 @@ module ActiveRecord
       else
         relation = spawn
         relation.select_values = column_names.map { |cn|
-          columns_hash.key?(cn) ? arel_table[cn] : cn
+          @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? klass.arel_attribute(cn, table) : cn
         }
         result = klass.connection.select_all(relation.arel, nil, bound_attributes)
         result.cast_values(klass.attribute_types)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 3f5d6de78a..5388c4cd65 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -147,7 +147,7 @@ module ActiveRecord
     def last(limit = nil)
       if limit
         if order_values.empty? && primary_key
-          order(arel_table[primary_key].desc).limit(limit).reverse
+          order(klass.arel_attribute(primary_key, table).desc).limit(limit).reverse
         else
           to_a.last(limit)
         end
@@ -514,7 +514,7 @@ module ActiveRecord
       # TODO: once the offset argument is removed from find_nth,
       # find_nth_with_limit_and_offset can be merged into this method
       relation = if order_values.empty? && primary_key
-                   order(arel_table[primary_key].asc)
+                   order(klass.arel_attribute(primary_key, table).asc)
                  else
                    self
                  end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
index 063150958a..a43478f815 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
@@ -3,7 +3,7 @@ module ActiveRecord
     class RelationHandler # :nodoc:
       def call(attribute, value)
         if value.select_values.empty?
-          value = value.select(value.klass.arel_table[value.klass.primary_key])
+          value = value.select(value.klass.arel_attribute(value.klass.primary_key, value.klass.arel_table))
         end
 
         attribute.in(value.arel)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 8ef9f9f627..ce03c0902b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1093,8 +1093,8 @@ module ActiveRecord
 
     def arel_columns(columns)
       columns.map do |field|
-        if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_clause.value
-          arel_table[field]
+        if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
+          klass.arel_attribute(field, table)
         elsif Symbol === field
           connection.quote_table_name(field.to_s)
         else
@@ -1105,7 +1105,7 @@ module ActiveRecord
 
     def reverse_sql_order(order_query)
       if order_query.empty?
-        return [table[primary_key].desc] if primary_key
+        return [klass.arel_attribute(primary_key, table).desc] if primary_key
         raise IrreversibleOrderError,
           "Relation has no current order and table has no primary key to be used as default order"
       end
@@ -1170,12 +1170,10 @@ module ActiveRecord
       order_args.map! do |arg|
         case arg
         when Symbol
-          arg = klass.attribute_alias(arg) if klass.attribute_alias?(arg)
-          table[arg].asc
+          klass.arel_attribute(arg, table).asc
         when Hash
           arg.map { |field, dir|
-            field = klass.attribute_alias(field) if klass.attribute_alias?(field)
-            table[field].send(dir.downcase)
+            klass.arel_attribute(field, table).send(dir.downcase)
           }
         else
           arg
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index f9bb1cf5e0..0faad48ce3 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -22,7 +22,11 @@ module ActiveRecord
     end
 
     def arel_attribute(column_name)
-      arel_table[column_name]
+      if klass
+        klass.arel_attribute(column_name, arel_table)
+      else
+        arel_table[column_name]
+      end
     end
 
     def type(column_name)
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index d0f60a84b5..ffb2da7a26 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -26,6 +26,10 @@ module ActiveRecord
       def sanitize_sql_for_order(sql)
         sql
       end
+
+      def arel_attribute(name, table)
+        table[name]
+      end
     end
 
     def relation
-- 
cgit v1.2.3