From e6a68a5cc3b47865abbba59bf693215c50136002 Mon Sep 17 00:00:00 2001
From: Pratik Naik <pratiknaik@gmail.com>
Date: Tue, 19 Jan 2010 04:32:40 +0530
Subject: Add Relation#find_with_associations to load relation with eager
 loaded associations

---
 activerecord/lib/active_record/relation.rb         | 29 +++--------------
 .../lib/active_record/relation/finder_methods.rb   | 36 ++++++++++++++++++++++
 2 files changed, 40 insertions(+), 25 deletions(-)

(limited to 'activerecord/lib')

diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 4ed118b02d..e37e692a97 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -45,33 +45,12 @@ module ActiveRecord
     def to_a
       return @records if loaded?
 
-      find_with_associations = @eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?)
-
-      @records = if find_with_associations
-        begin
-          options = {
-            :select => @select_values.join(", "),
-            :joins => arel.joins(arel),
-            :group =>  @group_values.join(", "),
-            :order => @order_values.join(', '),
-            :conditions => where_clause,
-            :limit => @limit_value,
-            :offset => @offset_value,
-            :from => @from_value
-          }
-
-          including = (@eager_load_values + @includes_values).uniq
-          join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
-          @klass.send(:find_with_associations, options, join_dependency)
-        rescue ThrowResult
-          []
-        end
-      else
-        @klass.find_by_sql(arel.to_sql)
-      end
+      eager_loading = @eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?)
+
+      @records = eager_loading ? find_with_associations : @klass.find_by_sql(arel.to_sql)
 
       preload = @preload_values
-      preload +=  @includes_values unless find_with_associations
+      preload +=  @includes_values unless eager_loading
       preload.each {|associations| @klass.send(:preload_associations, @records, associations) } 
 
       # @readonly_value is true only if set explicity. @implicit_readonly is true if there are JOINS and no explicit SELECT.
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 3668b0997f..980c5796f3 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -44,6 +44,42 @@ module ActiveRecord
 
     protected
 
+    def find_with_associations
+      including = (@eager_load_values + @includes_values).uniq
+      join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
+      rows = construct_relation_for_association_find(join_dependency).to_a
+      join_dependency.instantiate(rows)
+    rescue ThrowResult
+      []
+    end
+
+    def construct_relation_for_association_find(join_dependency)
+      relation = except(:includes, :eager_load, :preload, :select).select(@klass.send(:column_aliases, join_dependency))
+
+      for association in join_dependency.join_associations
+        relation = association.join_relation(relation)
+      end
+
+      limitable_reflections = @klass.send(:using_limitable_reflections?, join_dependency.reflections)
+
+      if !limitable_reflections && relation.limit_value
+        limited_id_condition = construct_limited_ids_condition(relation.except(:select))
+        relation = relation.where(limited_id_condition)
+      end
+
+      relation = relation.except(:limit, :offset) unless limitable_reflections
+
+      relation
+    end
+
+    def construct_limited_ids_condition(relation)
+      orders = relation.order_values.join(", ")
+      values = @klass.connection.distinct("#{@klass.connection.quote_table_name @klass.table_name}.#{@klass.primary_key}", orders)
+
+      ids_array = relation.select(values).collect {|row| row[@klass.primary_key]}
+      ids_array.empty? ? raise(ThrowResult) : primary_key.in(ids_array)
+    end
+
     def find_by_attributes(match, attributes, *args)
       conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h}
       result = where(conditions).send(match.finder)
-- 
cgit v1.2.3