Ticket #9640: ar_preload_polymorp_tests.diff
| File ar_preload_polymorp_tests.diff, 37.2 kB (added by fcheung, 7 months ago) |
|---|
-
activerecord/test/associations/eager_singularization_test.rb
old new 104 104 return unless @have_tables 105 105 assert_nothing_raised do 106 106 Virus.find(:all, :include => :octopus) 107 Virus.find(:all, :preload => :octopus) 107 108 end 108 109 end 109 110 … … 111 112 return unless @have_tables 112 113 assert_nothing_raised do 113 114 Octopus.find(:all, :include => :virus) 115 Octopus.find(:all, :preload => :virus) 114 116 end 115 117 end 116 118 … … 118 120 return unless @have_tables 119 121 assert_nothing_raised do 120 122 Bus.find(:all, :include => :passes) 123 Bus.find(:all, :preload => :passes) 121 124 end 122 125 end 123 126 … … 126 129 assert_nothing_raised do 127 130 Crisis.find(:all, :include => :messes) 128 131 Mess.find(:all, :include => :crises) 132 Crisis.find(:all, :preload => :messes) 133 Mess.find(:all, :preload => :crises) 129 134 end 130 135 end 131 136 … … 133 138 return unless @have_tables 134 139 assert_nothing_raised do 135 140 Crisis.find(:all, :include => :successes) 141 Crisis.find(:all, :preload => :successes) 136 142 end 137 143 end 138 144 … … 140 146 return unless @have_tables 141 147 assert_nothing_raised do 142 148 Crisis.find(:all, :include => :compresses) 149 Crisis.find(:all, :preload => :compresses) 143 150 end 144 151 end 145 152 end -
activerecord/test/associations/preload_test.rb
old new 1 require 'abstract_unit' 2 require 'fixtures/post' 3 require 'fixtures/tag' 4 require 'fixtures/comment' 5 require 'fixtures/author' 6 require 'fixtures/category' 7 require 'fixtures/categorization' 8 require 'fixtures/tagging' 9 require 'fixtures/company' 10 require 'fixtures/person' 11 require 'fixtures/reader' 12 13 class PreloadAssociationTest < Test::Unit::TestCase 14 fixtures :posts, :comments, :authors, :categories, :categories_posts, 15 :companies, :accounts, :tags, :people, :readers, :taggings, :categorizations, :items 16 17 def test_loading_with_one_association 18 posts = Post.find(:all, :preload => :comments) 19 post = posts.find { |p| p.id == 1 } 20 assert_no_queries do 21 assert_equal 2, post.comments.size 22 end 23 assert post.comments.include?(comments(:greetings)) 24 25 post = Post.find(:first, :preload => :comments, :conditions => "posts.title = 'Welcome to the weblog'") 26 assert_equal 2, post.comments.size 27 assert post.comments.include?(comments(:greetings)) 28 end 29 30 def test_with_ordering 31 list = Post.find(:all, :preload => :comments, :order => "posts.id DESC") 32 [:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments, 33 :authorless, :thinking, :welcome 34 ].each_with_index do |post, index| 35 assert_equal posts(post), list[index] 36 end 37 end 38 39 def test_loading_with_multiple_associations 40 posts = Post.find(:all, :preload => [ :comments, :author, :categories ], :order => "posts.id") 41 assert_equal 2, posts.first.comments.size 42 assert_equal 2, posts.first.categories.size 43 assert posts.first.comments.include?(comments(:greetings)) 44 end 45 46 def test_loading_from_an_association 47 posts = authors(:david).posts.find(:all, :preload => :comments, :order => "posts.id") 48 assert_equal 2, posts.first.comments.size 49 end 50 51 def test_loading_with_no_associations 52 assert_nil Post.find(posts(:authorless).id, :preload => :author).author 53 end 54 55 def test_eager_association_loading_with_belongs_to 56 comments = Comment.find(:all, :preload => :post) 57 assert_equal 10, comments.length 58 titles = comments.map { |c| c.post.title } 59 assert titles.include?(posts(:welcome).title) 60 assert titles.include?(posts(:sti_post_and_comments).title) 61 end 62 63 def test_eager_association_loading_with_belongs_to_and_limit 64 comments = Comment.find(:all, :preload => :post, :limit => 5, :order => 'comments.id') 65 assert_equal 5, comments.length 66 assert_equal [1,2,3,5,6], comments.collect { |c| c.id } 67 end 68 69 def test_eager_association_loading_with_belongs_to_and_limit_and_offset 70 comments = Comment.find(:all, :preload => :post, :limit => 3, :offset => 2, :order => 'comments.id') 71 assert_equal 3, comments.length 72 assert_equal [3,5,6], comments.collect { |c| c.id } 73 end 74 75 76 def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations 77 posts = Post.find(:all, :preload => [:author, :very_special_comment], :limit => 1, :order => 'posts.id') 78 assert_equal 1, posts.length 79 assert_equal [1], posts.collect { |p| p.id } 80 end 81 82 def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations 83 posts = Post.find(:all, :preload => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id') 84 assert_equal 1, posts.length 85 assert_equal [2], posts.collect { |p| p.id } 86 end 87 88 def test_eager_association_loading_with_explicit_join 89 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') 90 assert_equal 1, posts.length 91 end 92 93 def test_eager_with_has_many_through 94 posts_with_comments = people(:michael).posts.find(:all, :preload => :comments) 95 posts_with_author = people(:michael).posts.find(:all, :preload => :author ) 96 posts_with_comments_and_author = people(:michael).posts.find(:all, :preload => [ :comments, :author ]) 97 assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size } 98 assert_equal authors(:david), assert_no_queries { posts_with_author.first.author } 99 assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author } 100 end 101 102 def test_eager_with_has_many_through_an_sti_join_model 103 author = Author.find(:first, :preload => :special_post_comments, :order => 'authors.id') 104 assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments } 105 end 106 107 def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both 108 author = Author.find(:first, :preload => :special_nonexistant_post_comments, :order => 'authors.id') 109 assert_equal [], author.special_nonexistant_post_comments 110 end 111 112 def test_eager_with_has_many_through_join_model_with_conditions 113 assert_equal Author.find(:first, :preload => :hello_post_comments, 114 :order => 'authors.id').hello_post_comments.sort_by(&:id), 115 Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id) 116 end 117 118 def test_eager_with_has_many_and_limit 119 posts = Post.find(:all, :order => 'posts.id asc', :preload => [ :author, :comments ], :limit => 2) 120 assert_equal 2, posts.size 121 assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size } 122 end 123 124 def test_eager_with_has_many_and_limit_and_conditions_array 125 if current_adapter?(:OpenBaseAdapter) 126 posts = Post.find(:all, :preload => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id") 127 else 128 posts = Post.find(:all, :preload => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id") 129 end 130 assert_equal 2, posts.size 131 assert_equal [4,5], posts.collect { |p| p.id } 132 end 133 134 def test_eager_with_has_many_and_limit_with_no_results 135 posts = Post.find(:all, :preload => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'") 136 assert_equal 0, posts.size 137 end 138 139 def test_eager_with_has_and_belongs_to_many_and_limit 140 posts = Post.find(:all, :preload => :categories, :order => "posts.id", :limit => 3) 141 assert_equal 3, posts.size 142 assert_equal 2, posts[0].categories.size 143 assert_equal 1, posts[1].categories.size 144 assert_equal 0, posts[2].categories.size 145 assert posts[0].categories.include?(categories(:technology)) 146 assert posts[1].categories.include?(categories(:general)) 147 end 148 149 def test_eager_association_loading_with_habtm 150 posts = Post.find(:all, :preload => :categories, :order => "posts.id") 151 assert_equal 2, posts[0].categories.size 152 assert_equal 1, posts[1].categories.size 153 assert_equal 0, posts[2].categories.size 154 assert posts[0].categories.include?(categories(:technology)) 155 assert posts[1].categories.include?(categories(:general)) 156 end 157 158 def test_eager_with_inheritance 159 posts = SpecialPost.find(:all, :preload => [ :comments ]) 160 end 161 162 def test_eager_has_one_with_association_inheritance 163 post = Post.find(4, :preload => [ :very_special_comment ]) 164 assert_equal "VerySpecialComment", post.very_special_comment.class.to_s 165 end 166 167 def test_eager_has_many_with_association_inheritance 168 post = Post.find(4, :preload => [ :special_comments ]) 169 post.special_comments.each do |special_comment| 170 assert_equal "SpecialComment", special_comment.class.to_s 171 end 172 end 173 174 def test_eager_habtm_with_association_inheritance 175 post = Post.find(6, :preload => [ :special_categories ]) 176 assert_equal 1, post.special_categories.size 177 post.special_categories.each do |special_category| 178 assert_equal "SpecialCategory", special_category.class.to_s 179 end 180 end 181 182 def test_eager_with_has_one_dependent_does_not_destroy_dependent 183 assert_not_nil companies(:first_firm).account 184 f = Firm.find(:first, :preload => :account, 185 :conditions => ["companies.name = ?", "37signals"]) 186 assert_not_nil f.account 187 assert_equal companies(:first_firm, :reload).account, f.account 188 end 189 190 def test_eager_with_multi_table_conditional_properly_counts_the_records_when_using_size 191 author = authors(:david) 192 posts_with_no_comments = author.posts.select { |post| post.comments.blank? } 193 assert_equal posts_with_no_comments.size, author.posts_with_no_comments.size 194 assert_equal posts_with_no_comments, author.posts_with_no_comments 195 end 196 197 def test_eager_with_invalid_association_reference 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") { 205 post = Post.find(6, :preload=>[ 'monkeys' ]) 206 } 207 assert_raises(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :preload => :monkeys, :elephants") { 208 post = Post.find(6, :preload=>[ :monkeys, :elephants ]) 209 } 210 end 211 212 def find_all_ordered(className, include=nil) 213 className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :preload=>include) 214 end 215 216 def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm 217 # Eager includes of has many and habtm associations aren't necessarily sorted in the same way 218 def assert_equal_after_sort(item1, item2, item3 = nil) 219 assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) 220 assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3 221 end 222 # Test regular association, association with conditions, association with 223 # STI, and association with conditions assured not to be true 224 post_types = [:posts, :other_posts, :special_posts] 225 # test both has_many and has_and_belongs_to_many 226 [Author, Category].each do |className| 227 d1 = find_all_ordered(className) 228 # test including all post types at once 229 d2 = find_all_ordered(className, post_types) 230 d1.each_index do |i| 231 assert_equal(d1[i], d2[i]) 232 assert_equal_after_sort(d1[i].posts, d2[i].posts) 233 post_types[1..-1].each do |post_type| 234 # test including post_types together 235 d3 = find_all_ordered(className, [:posts, post_type]) 236 assert_equal(d1[i], d3[i]) 237 assert_equal_after_sort(d1[i].posts, d3[i].posts) 238 assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type)) 239 end 240 end 241 end 242 end 243 244 def test_eager_with_multiple_associations_with_same_table_has_one 245 d1 = find_all_ordered(Firm) 246 d2 = find_all_ordered(Firm, :account) 247 d1.each_index do |i| 248 assert_equal(d1[i], d2[i]) 249 assert_equal(d1[i].account, d2[i].account) 250 end 251 end 252 253 def test_eager_with_multiple_associations_with_same_table_belongs_to 254 firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition] 255 d1 = find_all_ordered(Client) 256 d2 = find_all_ordered(Client, firm_types) 257 d1.each_index do |i| 258 assert_equal(d1[i], d2[i]) 259 firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) } 260 end 261 end 262 263 def test_eager_with_valid_association_as_string_not_symbol 264 assert_nothing_raised { Post.find(:all, :preload => 'comments') } 265 end 266 267 def test_preload_has_one 268 post = Post.find(posts(:sti_comments).id, :preload => 'very_special_comment', :order => 'id') 269 assert_no_queries {post.very_special_comment.body} 270 assert_equal comments(:eager_sti_on_associations_vs_comment), post.very_special_comment 271 end 272 273 def test_preload_belongs_to 274 posts = Post.find(:all, :preload => 'author', :order => 'id', :conditions => 'author_id != 0') 275 assert_no_queries {posts.each {|post| post.author.name}} 276 assert_equal authors(:david), posts.first.author 277 end 278 279 def test_duplicate_middle_objects 280 comments = Comment.find :all, :conditions => 'post_id = 1', :preload => [:post => :author] 281 assert_no_queries do 282 comments.each {|comment| comment.post.author.name} 283 end 284 end 285 286 def test_finding_with_condition 287 firm = Firm.find(:first, :preload => :clients_like_ms) 288 assert_no_queries do 289 assert_equal "Microsoft", firm.clients_like_ms.first.name 290 end 291 end 292 293 def test_finding_with_condition_hash 294 firm = Firm.find(:first, :preload => :clients_like_ms_with_hash_conditions) 295 assert_no_queries do 296 assert_equal "Microsoft", firm.clients_like_ms_with_hash_conditions.first.name 297 end 298 end 299 300 def test_belongs_to_association_with_preload 301 author = posts(:welcome).author_with_preloaded_posts 302 assert_no_queries do 303 author.posts.length 304 end 305 end 306 307 def test_has_many_association_with_preload 308 comments = posts(:welcome).comments_with_post 309 comments.length 310 assert_no_queries do 311 comments.each {|comment| comment.post} 312 end 313 end 314 315 def test_hatbm_association_with_preload 316 categories = posts(:welcome).categories_with_categorizations 317 general= categories.sort_by(&:id).first 318 technology = categories.sort_by(&:id)[1] 319 320 assert_equal categories(:general), general 321 assert_equal categories(:technology), technology 322 323 expected = [categorizations(:david_welcome_general), categorizations(:mary_thinking_general)] 324 assert_no_queries do 325 assert_equal [], technology.categorizations 326 assert_equal expected, general.categorizations 327 end 328 end 329 330 def test_has_one_association_with_preload 331 comment = posts(:sti_comments).very_special_comment_with_preloaded_post 332 assert_no_queries do 333 assert_equal posts(:sti_comments), comment.post 334 end 335 end 336 337 338 def test_polymorphic_has_many 339 expected = taggings(:welcome_general) 340 p = Post.find(posts(:welcome).id, :preload => :taggings) 341 assert_no_queries {assert p.taggings.include?(expected)} 342 assert posts(:welcome).taggings.include?(taggings(:welcome_general)) 343 end 344 345 def test_polymorphic_has_one 346 expected = posts(:welcome) 347 348 tagging = Tagging.find(taggings(:welcome_general).id, :preload => :taggable) 349 assert_no_queries { assert_equal expected, tagging.taggable} 350 end 351 352 def test_polymorphic_belongs_to 353 p = Post.find(posts(:welcome).id, :preload => {:taggings => :taggable}) 354 assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable} 355 end 356 357 def test_preload_polymorphic_has_many_through 358 posts = Post.find(:all, :order => 'posts.id') 359 posts_with_tags = Post.find(:all, :preload => :tags, :order => 'posts.id') 360 assert_equal posts.length, posts_with_tags.length 361 posts.length.times do |i| 362 assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length } 363 end 364 end 365 366 def test_preload_polymorph_many_types 367 taggings = Tagging.find :all, :preload => :taggable, :conditions => ['taggable_type != ?', 'FakeModel'] 368 assert_no_queries do 369 taggings.first.taggable.id 370 taggings[1].taggable.id 371 end 372 assert_equal items(:dvd), taggings.last.taggable 373 assert_equal posts(:welcome), taggings.first.taggable 374 end 375 376 def test_preload_polymorphic_has_many 377 posts = Post.find(:all, :order => 'posts.id') 378 posts_with_taggings = Post.find(:all, :preload => :taggings, :order => 'posts.id') 379 assert_equal posts.length, posts_with_taggings.length 380 posts.length.times do |i| 381 assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length } 382 end 383 end 384 385 end -
activerecord/test/associations/cascaded_preload_test.rb
old new 1 require 'abstract_unit' 2 require 'fixtures/post' 3 require 'fixtures/comment' 4 require 'fixtures/author' 5 require 'fixtures/category' 6 require 'fixtures/categorization' 7 require 'fixtures/company' 8 require 'fixtures/topic' 9 require 'fixtures/reply' 10 11 class 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 79 end 80 81 require 'fixtures/vertex' 82 require 'fixtures/edge' 83 class 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 95 end -
activerecord/test/fixtures/post.rb
old new 6 6 end 7 7 8 8 belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts 9 belongs_to :author_with_preloaded_posts, :class_name => "Author", :foreign_key => :author_id, :preload => :posts 9 10 10 11 has_many :comments, :order => "body" do 11 12 def find_most_recent … … 15 16 16 17 has_one :very_special_comment 17 18 has_one :very_special_comment_with_post, :class_name => "VerySpecialComment", :include => :post 19 has_one :very_special_comment_with_preloaded_post, :class_name => "VerySpecialComment", :preload => :post 18 20 has_many :special_comments 19 21 20 22 has_and_belongs_to_many :categories 21 23 has_and_belongs_to_many :special_categories, :join_table => "categories_posts", :association_foreign_key => 'category_id' 24 has_and_belongs_to_many :categories_with_categorizations, :class_name => "Category", :join_table => "categories_posts", :join_table => "categories_posts", 25 :association_foreign_key => 'category_id', :preload => :categorizations 22 26 27 has_many :comments_with_post, :class_name => 'Comment', :preload => :post 28 23 29 has_many :taggings, :as => :taggable 24 30 has_many :tags, :through => :taggings, :include => :tagging do 25 31 def add_joins_and_select -
activerecord/lib/active_record/associations/association_proxy.rb
old new 114 114 :offset => @reflection.options[:offset], 115 115 :joins => @reflection.options[:joins], 116 116 :include => @reflection.options[:include], 117 :preload => @reflection.options[:preload], 117 118 :select => @reflection.options[:select] 118 119 ) 119 120 end -
activerecord/lib/active_record/associations/belongs_to_association.rb
old new 44 44 @reflection.klass.find( 45 45 @owner[@reflection.primary_key_name], 46 46 :conditions => conditions, 47 :include => @reflection.options[:include] 47 :include => @reflection.options[:include], 48 :preload => @reflection.options[:preload] 48 49 ) 49 50 end 50 51 -
activerecord/lib/active_record/associations/has_one_association.rb
old new 53 53 @reflection.klass.find(:first, 54 54 :conditions => @finder_sql, 55 55 :order => @reflection.options[:order], 56 :include => @reflection.options[:include] 56 :include => @reflection.options[:include], 57 :preload => @reflection.options[:preload] 57 58 ) 58 59 end 59 60 -
activerecord/lib/active_record/associations.rb
old new 1164 1164 :uniq, 1165 1165 :finder_sql, :counter_sql, 1166 1166 :before_add, :after_add, :before_remove, :after_remove, 1167 :extend 1167 :extend, :preload 1168 1168 ) 1169 1169 1170 1170 options[:extend] = create_extension_modules(association_id, extension, options[:extend]) if block_given? … … 1174 1174 1175 1175 def create_has_one_reflection(association_id, options) 1176 1176 options.assert_valid_keys( 1177 :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as 1177 :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :preload 1178 1178 ) 1179 1179 1180 1180 create_reflection(:has_one, association_id, options, self) … … 1183 1183 def create_belongs_to_reflection(association_id, options) 1184 1184 options.assert_valid_keys( 1185 1185 :class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent, 1186 :counter_cache, :extend, :polymorphic 1186 :counter_cache, :extend, :polymorphic, :preload 1187 1187 ) 1188 1188 1189 1189 reflection = create_reflection(:belongs_to, association_id, options, self) … … 1202 1202 :uniq, 1203 1203 :finder_sql, :delete_sql, :insert_sql, 1204 1204 :before_add, :after_add, :before_remove, :after_remove, 1205 :extend 1205 :extend, :preload 1206 1206 ) 1207 1207 1208 1208 options[:extend] = create_extension_modules(association_id, extension, options[:extend]) if block_given? -
activerecord/lib/active_record/base.rb
old new 428 428 # end 429 429 def find(*args) 430 430 options = args.extract_options! 431 associations_to_preload = options.delete(:preload) 431 432 validate_find_options(options) 432 433 set_readonly_option!(options) 433 434 434 case args.first435 results = case args.first 435 436 when :first then find_initial(options) 436 437 when :all then find_every(options) 437 438 else find_from_ids(args, options) 438 439 end 440 if associations_to_preload 441 preload_associations(results, associations_to_preload) 442 end 443 results 439 444 end 440 445 441 446 # Works like find(:all), but requires a complete SQL string. Examples: … … 1663 1668 quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) " 1664 1669 quoted_value 1665 1670 end 1671 1672 #loads the named associations for the activerecord object (or objects) given 1673 # 1674 # 1675 def preload_associations(top_objects, associations) 1676 top_objects = [top_objects].flatten.compact 1677 return if top_objects.empty? 1678 case associations 1679 when Array then associations.each {|association| preload_associations(top_objects, association)} 1680 when Symbol, String then preload_one_association(top_objects, associations.to_sym) 1681 when Hash then 1682 associations.each do |parent, child| 1683 raise "parent must be an association name" unless parent.is_a?( String) || parent.is_a?( Symbol) 1684 preload_associations(top_objects, parent) 1685 reflection = reflections[parent] 1686 parents = top_objects.map {|top_object| top_object.send(reflection.name)}.flatten 1687 unless parents.empty? 1688 parents.first.class.preload_associations(parents, child) 1689 end 1690 end 1691 end 1692 1693 end 1694 1695 def add_preloaded_object_to_collection(parent_object, reflection_name, associated_object) 1696 association_proxy = parent_object.send(reflection_name) 1697 association_proxy.loaded 1698 if associated_object.is_a? Array 1699 association_proxy.target.push(*associated_object) 1700 else 1701 association_proxy.target << associated_object 1702 end 1703 end 1704 1705 def preload_collection_object(id_to_object_map, reflection_name, associated_object, key) 1706 mapped_objects = id_to_object_map[associated_object[key].to_i] 1707 mapped_objects.each do |mapped_object| 1708 add_preloaded_object_to_collection(mapped_object, reflection_name, associated_object) 1709 end 1710 end 1711 1712 def preload_single_object(id_to_object_map, reflection_name, associated_object, key) 1713 mapped_objects = id_to_object_map[associated_object[key].to_i] 1714 mapped_objects.each do |mapped_object| 1715 mapped_object.send("set_#{reflection_name}_target", associated_object) 1716 end 1717 end 1718 1719 1720 def has_n_preload_conditions(reflection) 1721 options = reflection.options 1722 if interface = reflection.options[:as] 1723 conditions = "#{reflection.klass.table_name}.#{interface}_id IN (?) and #{reflection.klass.table_name}.#{interface}_type = '#{self.name}'" 1724 else 1725 foreign_key = options[:foreign_key] || reflection.active_record.to_s.foreign_key 1726 conditions = "#{reflection.klass.table_name}.#{foreign_key} IN (?)" 1727 end 1728 end 1729 1730 def preload_one_association(top_objects, association) 1731 reflection = reflections[association] 1732 raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection 1733 1734 reflection_name = reflection.name 1735 options = reflection.options 1736 table_name = reflection.klass.table_name unless options[:polymorphic] 1737 primary_key_name = reflection.primary_key_name 1738 foreign_key = options[:foreign_key] || reflection.active_record.to_s.foreign_key 1739 if options[:as] 1740 foreign_key = "#{options[:as]}_id" 1741 end 1742 #it is not enough to just do index_by &:id as there could be duplicates (see test_duplicate_middle_objects) 1743 1744 id_to_object_map = {} 1745 ids = [] 1746 top_objects.each do |top_object| 1747 ids << top_object.id 1748 mapped_objects = (id_to_object_map[top_object.id] ||= []) 1749 mapped_objects << top_object 1750 end 1751 ids = top_objects.uniq 1752 1753 case reflection.macro 1754 when :belongs_to 1755 if options[:polymorphic] 1756 polymorph_type = options[:foreign_type] 1757 klasses = top_objects.map {|object| object.send polymorph_type}.uniq.compact.map &:constantize 1758 else 1759 klasses = [reflection.klass] 1760 end 1761 klasses.each do |klass| 1762 table_name = klass.table_name 1763 conditions = "t0.#{self.primary_key} IN (?)" 1764 conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 1765 1766 joins = "INNER JOIN #{self.table_name} AS t0 ON #{table_name}.#{primary_key} = t0.#{primary_key_name}" 1767 if options[:polymorphic] 1768 joins << " AND #{polymorph_type} = '#{klass.name}'" 1769 end 1770 associated_objects = klass.find(:all, :conditions => [conditions, ids], 1771 :joins => joins, 1772 :preload => options[:preload], 1773 :select => "#{options[:select] || table_name+'.*'}, t0.#{self.primary_key} as _parent_object_id", 1774 :order => options[:order]) 1775 associated_objects.each do |associated_object| 1776 preload_single_object(id_to_object_map, reflection_name, associated_object, '_parent_object_id') 1777 end 1778 end 1779 when :has_one 1780 top_objects.each {|object| object.send("set_#{reflection.name}_target", nil)} 1781 conditions = has_n_preload_conditions(reflection) 1782 conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 1783 associated_objects = reflection.klass.find(:all, :conditions => [conditions, ids], 1784 :select => (options[:select] || "#{table_name}.*"), 1785 :preload => options[:preload], 1786 :order => options[:order]) 1787 associated_objects.each do |associated_object| 1788 preload_single_object(id_to_object_map, reflection_name, associated_object, foreign_key) 1789 end 1790 1791 when :has_many 1792 top_objects.each {|object| object.send(reflection.name).loaded} 1793 1794 if options[:through] 1795 through_reflection = reflections[options[:through]] 1796 through_primary_key = through_reflection.primary_key_name 1797 top_objects.first.class.preload_one_association(top_objects, options[:through]) 1798 through_objects = top_objects.compact.map {|object| object.send options[:through]}.flatten 1799 1800 unless through_objects.empty? 1801 source = reflection.source_reflection.name 1802 through_objects.first.class.preload_one_association(through_objects, source) 1803 1804 through_objects.compact.each do |through_object| 1805 associated_object = through_object.send(source) 1806 mapped_objects = id_to_object_map[through_object[through_primary_key].to_i] 1807 mapped_objects.each do |mapped_object| 1808 add_preloaded_object_to_collection(mapped_object, reflection_name, associated_object) 1809 end 1810 end 1811 end 1812 1813 else 1814 conditions = has_n_preload_conditions(reflection) 1815 conditions << " AND (#{sanitize_sql options[:conditions]})" if options[:conditions] 1816 associated_objects = reflection.klass.find(:all, :conditions => [conditions, ids], 1817 :select => (options[:select] || "#{table_name}.*"), 1818 :preload => options[:preload], 1819 :order => options[:order]) 1820 associated_objects.each do |associated_object| 1821 preload_collection_object(id_to_object_map, reflection_name, associated_object, foreign_key) 1822 end 1823 end 1824 when :has_and_belongs_to_many 1825 top_objects.each {|object| object.send(reflection.name).loaded} 1826 conditions = "t0.#{reflection.primary_key_name} IN (?)" 1827 conditions << " AND (#{options[:conditions]})" if options[:conditions] 1828 associated_objects = reflection.klass.find(:all, :conditions => [conditions, ids], 1829 :preload => options[:preload], 1830 :joins => "INNER JOIN #{options[:join_table]} as t0 ON #{reflection.klass.table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", 1831 :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as _parent_object_id", 1832 :order => options[:order]) 1833 associated_objects.each do |associated_object| 1834 preload_collection_object(id_to_object_map, reflection_name, associated_object, '_parent_object_id') 1835 end 1836 else 1837 raise "Unsupported association type #{reflection.name}!" 1838 end 1839 1840 end 1841 1666 1842 end 1667 1843 1668 1844 public