Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #9640: preload_ar.3.2.diff

File preload_ar.3.2.diff, 26.0 kB (added by fcheung, 8 months ago)

added tests for conditions

  • activerecord/test/associations/preload_test.rb

    old new  
     1require 'abstract_unit' 
     2require 'fixtures/post' 
     3require 'fixtures/comment' 
     4require 'fixtures/author' 
     5require 'fixtures/category' 
     6require 'fixtures/company' 
     7require 'fixtures/person' 
     8require 'fixtures/reader' 
     9 
     10class PreloadAssociationTest < Test::Unit::TestCase 
     11  fixtures :posts, :comments, :authors, :categories, :categories_posts, 
     12            :companies, :accounts, :tags, :people, :readers 
     13 
     14  def test_loading_with_one_association 
     15    posts = Post.find(:all, :preload => :comments) 
     16    post = posts.find { |p| p.id == 1 } 
     17    assert_no_queries do 
     18      assert_equal 2, post.comments.size 
     19    end 
     20    assert post.comments.include?(comments(:greetings)) 
     21 
     22    post = Post.find(:first, :preload => :comments, :conditions => "posts.title = 'Welcome to the weblog'") 
     23    assert_equal 2, post.comments.size 
     24    assert post.comments.include?(comments(:greetings)) 
     25  end 
     26 
     27  def test_with_ordering 
     28    list = Post.find(:all, :preload => :comments, :order => "posts.id DESC") 
     29    [:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments, 
     30     :authorless, :thinking, :welcome 
     31    ].each_with_index do |post, index| 
     32      assert_equal posts(post), list[index] 
     33    end 
     34  end 
     35 
     36  def test_loading_with_multiple_associations 
     37    posts = Post.find(:all, :preload => [ :comments, :author, :categories ], :order => "posts.id") 
     38    assert_equal 2, posts.first.comments.size 
     39    assert_equal 2, posts.first.categories.size 
     40    assert posts.first.comments.include?(comments(:greetings)) 
     41  end 
     42 
     43  def test_loading_from_an_association 
     44    posts = authors(:david).posts.find(:all, :preload => :comments, :order => "posts.id") 
     45    assert_equal 2, posts.first.comments.size 
     46  end 
     47 
     48  def test_loading_with_no_associations 
     49    assert_nil Post.find(posts(:authorless).id, :preload => :author).author 
     50  end 
     51 
     52  def test_eager_association_loading_with_belongs_to 
     53    comments = Comment.find(:all, :preload => :post) 
     54    assert_equal 10, comments.length 
     55    titles = comments.map { |c| c.post.title } 
     56    assert titles.include?(posts(:welcome).title) 
     57    assert titles.include?(posts(:sti_post_and_comments).title) 
     58  end 
     59   
     60  def test_eager_association_loading_with_belongs_to_and_limit 
     61    comments = Comment.find(:all, :preload => :post, :limit => 5, :order => 'comments.id') 
     62    assert_equal 5, comments.length 
     63    assert_equal [1,2,3,5,6], comments.collect { |c| c.id } 
     64  end 
     65 
     66  def test_eager_association_loading_with_belongs_to_and_limit_and_offset 
     67    comments = Comment.find(:all, :preload => :post, :limit => 3, :offset => 2, :order => 'comments.id') 
     68    assert_equal 3, comments.length 
     69    assert_equal [3,5,6], comments.collect { |c| c.id } 
     70  end 
     71 
     72   
     73  def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations 
     74    posts = Post.find(:all, :preload => [:author, :very_special_comment], :limit => 1, :order => 'posts.id') 
     75    assert_equal 1, posts.length 
     76    assert_equal [1], posts.collect { |p| p.id } 
     77  end 
     78   
     79  def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations 
     80    posts = Post.find(:all, :preload => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id') 
     81    assert_equal 1, posts.length 
     82    assert_equal [2], posts.collect { |p| p.id } 
     83  end 
     84   
     85  def test_eager_association_loading_with_explicit_join 
     86    posts = Post.find(:all, :preload => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id') 
     87    assert_equal 1, posts.length 
     88  end 
     89   
     90  def test_eager_with_has_many_through 
     91    posts_with_comments = people(:michael).posts.find(:all, :preload => :comments) 
     92    posts_with_author = people(:michael).posts.find(:all, :preload => :author ) 
     93    posts_with_comments_and_author = people(:michael).posts.find(:all, :preload => [ :comments, :author ]) 
     94    assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size } 
     95    assert_equal authors(:david), assert_no_queries { posts_with_author.first.author } 
     96    assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author } 
     97  end 
     98 
     99  def test_eager_with_has_many_through_an_sti_join_model 
     100    author = Author.find(:first, :preload => :special_post_comments, :order => 'authors.id') 
     101    assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments } 
     102  end 
     103   
     104  def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both 
     105    author = Author.find(:first, :preload => :special_nonexistant_post_comments, :order => 'authors.id') 
     106    assert_equal [], author.special_nonexistant_post_comments 
     107  end 
     108 
     109  def test_eager_with_has_many_through_join_model_with_conditions 
     110    assert_equal Author.find(:first, :preload => :hello_post_comments, 
     111                             :order => 'authors.id').hello_post_comments.sort_by(&:id), 
     112                 Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id) 
     113  end 
     114 
     115  def test_eager_with_has_many_and_limit 
     116    posts = Post.find(:all, :order => 'posts.id asc', :preload => [ :author, :comments ], :limit => 2) 
     117    assert_equal 2, posts.size 
     118    assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size } 
     119  end 
     120 
     121  def test_eager_with_has_many_and_limit_and_conditions_array 
     122    if current_adapter?(:OpenBaseAdapter) 
     123      posts = Post.find(:all, :preload => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id") 
     124    else 
     125      posts = Post.find(:all, :preload => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id") 
     126    end 
     127    assert_equal 2, posts.size 
     128    assert_equal [4,5], posts.collect { |p| p.id }     
     129  end 
     130 
     131  def test_eager_with_has_many_and_limit_with_no_results 
     132    posts = Post.find(:all, :preload => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'") 
     133    assert_equal 0, posts.size 
     134  end 
     135   
     136  def test_eager_with_has_and_belongs_to_many_and_limit 
     137    posts = Post.find(:all, :preload => :categories, :order => "posts.id", :limit => 3) 
     138    assert_equal 3, posts.size 
     139    assert_equal 2, posts[0].categories.size 
     140    assert_equal 1, posts[1].categories.size 
     141    assert_equal 0, posts[2].categories.size 
     142    assert posts[0].categories.include?(categories(:technology)) 
     143    assert posts[1].categories.include?(categories(:general)) 
     144  end 
     145 
     146  def test_eager_association_loading_with_habtm 
     147    posts = Post.find(:all, :preload => :categories, :order => "posts.id") 
     148    assert_equal 2, posts[0].categories.size 
     149    assert_equal 1, posts[1].categories.size 
     150    assert_equal 0, posts[2].categories.size 
     151    assert posts[0].categories.include?(categories(:technology)) 
     152    assert posts[1].categories.include?(categories(:general)) 
     153  end 
     154 
     155  def test_eager_with_inheritance 
     156    posts = SpecialPost.find(:all, :preload => [ :comments ]) 
     157  end 
     158 
     159  def test_eager_has_one_with_association_inheritance 
     160    post = Post.find(4, :preload => [ :very_special_comment ]) 
     161    assert_equal "VerySpecialComment", post.very_special_comment.class.to_s 
     162  end 
     163 
     164  def test_eager_has_many_with_association_inheritance 
     165    post = Post.find(4, :preload => [ :special_comments ]) 
     166    post.special_comments.each do |special_comment| 
     167      assert_equal "SpecialComment", special_comment.class.to_s 
     168    end 
     169  end 
     170 
     171  def test_eager_habtm_with_association_inheritance 
     172    post = Post.find(6, :preload => [ :special_categories ]) 
     173    assert_equal 1, post.special_categories.size 
     174    post.special_categories.each do |special_category| 
     175      assert_equal "SpecialCategory", special_category.class.to_s 
     176    end 
     177  end 
     178 
     179  def test_eager_with_has_one_dependent_does_not_destroy_dependent 
     180    assert_not_nil companies(:first_firm).account 
     181    f = Firm.find(:first, :preload => :account, 
     182            :conditions => ["companies.name = ?", "37signals"]) 
     183    assert_not_nil f.account 
     184    assert_equal companies(:first_firm, :reload).account, f.account 
     185  end 
     186   
     187  def test_eager_with_multi_table_conditional_properly_counts_the_records_when_using_size 
     188    author = authors(:david) 
     189    posts_with_no_comments = author.posts.select { |post| post.comments.blank? } 
     190    assert_equal posts_with_no_comments.size, author.posts_with_no_comments.size 
     191    assert_equal posts_with_no_comments, author.posts_with_no_comments 
     192  end 
     193 
     194  def test_eager_with_invalid_association_reference 
     195    assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :preload => :monkeys") { 
     196      post = Post.find(6, :preload=> :monkeys ) 
     197    } 
     198    assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :preload => :monkeys") { 
     199      post = Post.find(6, :preload=>[ :monkeys ]) 
     200    } 
     201    assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :preload => :monkeys") { 
     202      post = Post.find(6, :preload=>[ 'monkeys' ]) 
     203    } 
     204    assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it?  You specified :preload => :monkeys, :elephants") { 
     205      post = Post.find(6, :preload=>[ :monkeys, :elephants ]) 
     206    } 
     207  end 
     208   
     209  def find_all_ordered(className, include=nil) 
     210    className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :preload=>include) 
     211  end 
     212     
     213  def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm 
     214    # Eager includes of has many and habtm associations aren't necessarily sorted in the same way 
     215    def assert_equal_after_sort(item1, item2, item3 = nil) 
     216      assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) 
     217      assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3 
     218    end 
     219    # Test regular association, association with conditions, association with 
     220    # STI, and association with conditions assured not to be true 
     221    post_types = [:posts, :other_posts, :special_posts] 
     222    # test both has_many and has_and_belongs_to_many 
     223    [Author, Category].each do |className| 
     224      d1 = find_all_ordered(className) 
     225      # test including all post types at once 
     226      d2 = find_all_ordered(className, post_types)  
     227      d1.each_index do |i|  
     228        assert_equal(d1[i], d2[i]) 
     229        assert_equal_after_sort(d1[i].posts, d2[i].posts) 
     230        post_types[1..-1].each do |post_type| 
     231          # test including post_types together 
     232          d3 = find_all_ordered(className, [:posts, post_type]) 
     233          assert_equal(d1[i], d3[i]) 
     234          assert_equal_after_sort(d1[i].posts, d3[i].posts) 
     235          assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type)) 
     236        end 
     237      end 
     238    end 
     239  end 
     240   
     241  def test_eager_with_multiple_associations_with_same_table_has_one 
     242    d1 = find_all_ordered(Firm) 
     243    d2 = find_all_ordered(Firm, :account) 
     244    d1.each_index do |i|  
     245      assert_equal(d1[i], d2[i]) 
     246      assert_equal(d1[i].account, d2[i].account) 
     247    end 
     248  end 
     249   
     250  def test_eager_with_multiple_associations_with_same_table_belongs_to 
     251    firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition] 
     252    d1 = find_all_ordered(Client) 
     253    d2 = find_all_ordered(Client, firm_types) 
     254    d1.each_index do |i|  
     255      assert_equal(d1[i], d2[i]) 
     256      firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) } 
     257    end 
     258  end 
     259   
     260  def test_eager_with_valid_association_as_string_not_symbol 
     261    assert_nothing_raised { Post.find(:all, :preload => 'comments') } 
     262  end 
     263   
     264  def test_preload_has_one 
     265    post = Post.find(posts(:sti_comments).id, :preload => 'very_special_comment', :order => 'id') 
     266    assert_no_queries {post.very_special_comment.body} 
     267    assert_equal comments(:eager_sti_on_associations_vs_comment), post.very_special_comment 
     268  end 
     269   
     270  def test_preload_belongs_to 
     271    posts = Post.find(:all, :preload => 'author', :order => 'id', :conditions => 'author_id != 0') 
     272    assert_no_queries {posts.each {|post| post.author.name}} 
     273    assert_equal authors(:david), posts.first.author 
     274  end 
     275   
     276  def test_duplicate_middle_objects 
     277    comments = Comment.find :all, :conditions => 'post_id = 1', :preload => [:post => :author] 
     278    assert_no_queries do 
     279      comments.each {|comment| comment.post.author.name} 
     280    end 
     281  end 
     282   
     283  def test_finding_with_condition 
     284    firm = Firm.find(:first, :preload => :clients_like_ms) 
     285    assert_no_queries do 
     286      assert_equal "Microsoft", firm.clients_like_ms.first.name 
     287    end 
     288  end 
     289 
     290  def test_finding_with_condition_hash 
     291    firm = Firm.find(:first, :preload => :clients_like_ms_with_hash_conditions) 
     292    assert_no_queries do 
     293      assert_equal "Microsoft", firm.clients_like_ms_with_hash_conditions.first.name 
     294    end 
     295  end 
     296   
     297end 
  • activerecord/test/associations/cascaded_preload_test.rb

    old new  
     1require 'abstract_unit' 
     2require 'fixtures/post' 
     3require 'fixtures/comment' 
     4require 'fixtures/author' 
     5require 'fixtures/category' 
     6require 'fixtures/categorization' 
     7require 'fixtures/company' 
     8require 'fixtures/topic' 
     9require 'fixtures/reply' 
     10 
     11class CascadedPreloadTest < Test::Unit::TestCase 
     12  fixtures :authors, :mixins, :companies, :posts, :topics 
     13 
     14  def test_eager_association_loading_with_cascaded_two_levels 
     15    authors = Author.find(:all, :preload=>{:posts=>:comments}, :order=>"authors.id") 
     16    assert_equal 2, authors.size 
     17    assert_equal 5, authors[0].posts.size 
     18    assert_equal 1, authors[1].posts.size 
     19    assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i} 
     20  end 
     21 
     22  def test_eager_association_loading_with_cascaded_two_levels_and_one_level 
     23    authors = Author.find(:all, :preload=>[{:posts=>:comments}, :categorizations], :order=>"authors.id") 
     24    assert_equal 2, authors.size 
     25    assert_equal 5, authors[0].posts.size 
     26    assert_equal 1, authors[1].posts.size 
     27    assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i} 
     28    assert_equal 1, authors[0].categorizations.size 
     29    assert_equal 2, authors[1].categorizations.size 
     30  end 
     31 
     32  def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations 
     33    authors = Author.find(:all, :preload=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id") 
     34    assert_no_queries do 
     35      assert_equal 2, authors.size 
     36      assert_equal 5, authors[0].posts.size 
     37      assert_equal 1, authors[1].posts.size 
     38      assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i} 
     39    end 
     40  end 
     41 
     42  def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference 
     43    authors = Author.find(:all, :preload=>{:posts=>[:comments, :author]}, :order=>"authors.id") 
     44    assert_equal 2, authors.size 
     45    assert_equal 5, authors[0].posts.size 
     46    assert_equal authors(:david).name, authors[0].name 
     47    assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq 
     48  end 
     49 
     50  def test_eager_association_loading_with_cascaded_two_levels_with_condition 
     51    authors = Author.find(:all, :preload=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id") 
     52    assert_equal 1, authors.size 
     53    assert_equal 5, authors[0].posts.size 
     54  end 
     55 
     56  def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong 
     57    firms = Firm.find(:all, :preload=>{:account=>{:firm=>:account}}, :order=>"companies.id") 
     58    assert_equal 2, firms.size 
     59    assert_equal firms.first.account, firms.first.account.firm.account 
     60    assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account } 
     61    assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account } 
     62  end 
     63 
     64  def test_eager_association_loading_with_has_many_sti 
     65    topics = Topic.find(:all, :preload => :replies, :order => 'topics.id') 
     66    assert_equal topics(:first, :second), topics 
     67    assert_no_queries do 
     68      assert_equal 1, topics[0].replies.size 
     69      assert_equal 0, topics[1].replies.size 
     70    end 
     71  end 
     72 
     73  def test_eager_association_loading_with_belongs_to_sti 
     74    replies = Reply.find(:all, :preload => :topic, :order => 'topics.id') 
     75    assert_equal [topics(:second)], replies 
     76    assert_equal topics(:first), assert_no_queries { replies.first.topic } 
     77  end 
     78 
     79end 
     80 
     81require 'fixtures/vertex' 
     82require 'fixtures/edge' 
     83class CascadedPreloadTest < Test::Unit::TestCase 
     84  fixtures :edges, :vertices 
     85 
     86  def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through 
     87    source = Vertex.find(:first, :preload=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id') 
     88    assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first } 
     89  end 
     90 
     91  def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many 
     92    sink = Vertex.find(:first, :preload=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC') 
     93    assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first } 
     94  end 
     95end 
  • activerecord/lib/active_record/base.rb

    old new  
    419419      #   end 
    420420      def find(*args) 
    421421        options = args.extract_options! 
     422        associations_to_preload = options.delete(:preload) 
    422423        validate_find_options(options) 
    423424        set_readonly_option!(options) 
    424425 
    425         case args.first 
     426        results = case args.first 
    426427          when :first then find_initial(options) 
    427428          when :all   then find_every(options) 
    428429          else             find_from_ids(args, options) 
    429430        end 
     431        if associations_to_preload 
     432          preload_associations(results, associations_to_preload) 
     433        end 
     434        results 
    430435      end 
    431436       
    432437      # Works like find(:all), but requires a complete SQL string. Examples: 
     
    16491654          quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "  
    16501655          quoted_value  
    16511656        end 
     1657         
     1658        #loads the named associations for the activerecord object (or objects) given 
     1659        # 
     1660        # 
     1661        def preload_associations(top_objects, associations) 
     1662          top_objects = [top_objects].flatten.compact 
     1663          case associations 
     1664          when Array then associations.each {|association| preload_associations(top_objects, association)} 
     1665          when Symbol, String then preload_one_association(top_objects, associations.to_sym) 
     1666          when Hash then 
     1667            associations.each do |parent, child| 
     1668              raise "parent must be an association name" unless parent.is_a?( String) || parent.is_a?( Symbol) 
     1669              preload_associations(top_objects, parent) 
     1670              reflection = reflections[parent] 
     1671              parents = top_objects.map {|top_object| top_object.send(reflection.name)}.flatten 
     1672              unless parents.empty? 
     1673                parents.first.class.preload_associations(parents, child) 
     1674              end 
     1675            end 
     1676          end 
     1677           
     1678        end 
     1679         
     1680        def add_preloaded_object_to_collection(parent_object, reflection_name, associated_object) 
     1681          association_proxy = parent_object.send(reflection_name) 
     1682          association_proxy.loaded 
     1683          if associated_object.is_a? Array 
     1684            association_proxy.target.push(*associated_object) 
     1685          else 
     1686            association_proxy.target << associated_object 
     1687          end 
     1688        end 
     1689           
     1690        def preload_collection_object(id_to_object_map, reflection_name, associated_object, key) 
     1691          mapped_objects = id_to_object_map[associated_object[key].to_i] 
     1692          mapped_objects.each do |mapped_object| 
     1693            add_preloaded_object_to_collection(mapped_object, reflection_name, associated_object) 
     1694          end 
     1695        end 
     1696 
     1697        def preload_single_object(id_to_object_map, reflection_name, associated_object, key) 
     1698          mapped_objects = id_to_object_map[associated_object[key].to_i] 
     1699          mapped_objects.each do |mapped_object| 
     1700            mapped_object.send("set_#{reflection_name}_target", associated_object) 
     1701          end 
     1702        end 
     1703         
     1704        def preload_one_association(top_objects, association)  
     1705          reflection = reflections[association] 
     1706          raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection 
     1707                     
     1708          reflection_name = reflection.name 
     1709          options = reflection.options 
     1710          table_name = reflection.klass.table_name 
     1711          primary_key_name = reflection.primary_key_name 
     1712          foreign_key = options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key 
     1713                    
     1714          #it is not enough to just do  index_by &:id as there could be duplicates (see test_duplicate_middle_objects) 
     1715           
     1716          id_to_object_map = {} 
     1717          ids = [] 
     1718          top_objects.each do |top_object| 
     1719            ids << top_object.id 
     1720            mapped_objects = (id_to_object_map[top_object.id] ||= []) 
     1721            mapped_objects << top_object 
     1722          end 
     1723          ids = top_objects.uniq 
     1724           
     1725          case reflection.macro 
     1726          when :belongs_to 
     1727            conditions = "t0.#{self.primary_key} IN (?)" 
     1728            conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 
     1729            associated_objects = reflection.klass.find(:all, :conditions => [conditions, ids], 
     1730            :joins => "INNER JOIN #{self.table_name} AS t0 ON #{table_name}.#{primary_key} = t0.#{primary_key_name}", 
     1731            :select => "#{options[:select] || table_name+'.*'}, t0.#{self.primary_key} as _parent_object_id", 
     1732            :order => options[:order]) 
     1733            associated_objects.each do |associated_object| 
     1734              preload_single_object(id_to_object_map, reflection_name, associated_object, '_parent_object_id') 
     1735            end 
     1736             
     1737          when :has_one 
     1738            top_objects.each {|object| object.send("set_#{reflection.name}_target", nil)} 
     1739            conditions = "#{table_name}.#{foreign_key} IN (?)" 
     1740            conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 
     1741            associated_objects =  reflection.klass.find(:all, :conditions => [conditions, ids], 
     1742            :select => (options[:select] || "#{table_name}.*"), 
     1743            :order => options[:order]) 
     1744            associated_objects.each do |associated_object| 
     1745              preload_single_object(id_to_object_map, reflection_name, associated_object, foreign_key) 
     1746            end 
     1747 
     1748          when :has_many 
     1749            top_objects.each {|object| object.send(reflection.name).loaded} 
     1750             
     1751            if options[:through] 
     1752              through_reflection = reflections[options[:through]]               
     1753              through_primary_key = through_reflection.primary_key_name 
     1754              top_objects.first.class.preload_one_association(top_objects, options[:through]) 
     1755              through_objects = top_objects.compact.map {|object| object.send options[:through]}.flatten 
     1756               
     1757              unless through_objects.empty? 
     1758                source = reflection.source_reflection.name 
     1759                through_objects.first.class.preload_one_association(through_objects, source) 
     1760               
     1761                through_objects.compact.each do |through_object| 
     1762                  associated_object = through_object.send(source) 
     1763                  mapped_objects = id_to_object_map[through_object[through_primary_key].to_i] 
     1764                  mapped_objects.each do |mapped_object| 
     1765                    add_preloaded_object_to_collection(mapped_object, reflection_name, associated_object) 
     1766                  end                   
     1767                end 
     1768              end 
     1769               
     1770            else 
     1771              conditions = "#{table_name}.#{foreign_key} IN (?)" 
     1772              conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 
     1773              associated_objects =  reflection.klass.find(:all, :conditions => [conditions, ids], 
     1774                                  :select => (options[:select] || "#{table_name}.*"), 
     1775                                  :order => options[:order]) 
     1776              associated_objects.each do |associated_object| 
     1777                preload_collection_object(id_to_object_map, reflection_name, associated_object, foreign_key) 
     1778              end 
     1779            end 
     1780          when :has_and_belongs_to_many 
     1781            top_objects.each {|object| object.send(reflection.name).loaded} 
     1782            conditions = "t0.#{reflection.primary_key_name}  IN (?)" 
     1783            conditions << " AND (#{options[:conditions]})" if options[:conditions] 
     1784            associated_objects = reflection.klass.find(:all, :conditions => [conditions, ids], 
     1785            :joins => "INNER JOIN #{options[:join_table]} as t0 ON #{reflection.klass.table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", 
     1786            :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as _parent_object_id", 
     1787            :order => options[:order]) 
     1788            associated_objects.each do |associated_object| 
     1789              preload_collection_object(id_to_object_map, reflection_name, associated_object, '_parent_object_id') 
     1790            end 
     1791          else 
     1792            raise "Unsupported association type #{reflection.name}!" 
     1793          end 
     1794           
     1795        end 
     1796         
    16521797    end 
    16531798 
    16541799    public