| | 1 | require 'abstract_unit' |
|---|
| | 2 | require 'fixtures/post' |
|---|
| | 3 | require 'fixtures/comment' |
|---|
| | 4 | require 'fixtures/author' |
|---|
| | 5 | require 'fixtures/category' |
|---|
| | 6 | require 'fixtures/company' |
|---|
| | 7 | require 'fixtures/person' |
|---|
| | 8 | require 'fixtures/reader' |
|---|
| | 9 | |
|---|
| | 10 | class 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 | end |