bullet 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require 'bullet'
@@ -0,0 +1,96 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
+ # This test is just used for http://github.com/flyerhzm/bullet/issues/#issue/14
5
+ describe Bullet::Detector::Association do
6
+
7
+ describe "for chris" do
8
+ def setup_db
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+ create_table :locations do |t|
11
+ t.column :name, :string
12
+ end
13
+
14
+ create_table :hotels do |t|
15
+ t.column :name, :string
16
+ t.column :location_id, :integer
17
+ end
18
+
19
+ create_table :deals do |t|
20
+ t.column :name, :string
21
+ t.column :hotel_id, :integer
22
+ end
23
+ end
24
+ end
25
+
26
+ def teardown_db
27
+ ActiveRecord::Base.connection.tables.each do |table|
28
+ ActiveRecord::Base.connection.drop_table(table)
29
+ end
30
+ end
31
+
32
+ class Location < ActiveRecord::Base
33
+ has_many :hotels
34
+ end
35
+
36
+ class Hotel < ActiveRecord::Base
37
+ belongs_to :location
38
+ has_many :deals
39
+ end
40
+
41
+ class Deal < ActiveRecord::Base
42
+ belongs_to :hotel
43
+ has_one :location, :through => :hotel
44
+ end
45
+
46
+ before(:all) do
47
+ setup_db
48
+
49
+ location1 = Location.create(:name => "location1")
50
+ location2 = Location.create(:name => "location2")
51
+
52
+ hotel1 = location1.hotels.create(:name => "hotel1")
53
+ hotel2 = location1.hotels.create(:name => "hotel2")
54
+ hotel3 = location2.hotels.create(:name => "hotel3")
55
+ hotel4 = location2.hotels.create(:name => "hotel4")
56
+
57
+ deal1 = hotel1.deals.create(:name => "deal1")
58
+ deal2 = hotel2.deals.create(:name => "deal2")
59
+ deal3 = hotel3.deals.create(:name => "deal3")
60
+ deal4 = hotel4.deals.create(:name => "deal4")
61
+ end
62
+
63
+ after(:all) do
64
+ teardown_db
65
+ end
66
+
67
+ before(:each) do
68
+ Bullet.start_request
69
+ end
70
+
71
+ after(:each) do
72
+ Bullet.end_request
73
+ end
74
+
75
+ it "should detect unpreload association from deal to hotel" do
76
+ Deal.all.each do |deal|
77
+ deal.hotel.location.name
78
+ end
79
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Deal, :hotel)
80
+ end
81
+
82
+ it "should detect unpreload association from hotel to location" do
83
+ Deal.includes(:hotel).each do |deal|
84
+ deal.hotel.location.name
85
+ end
86
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Hotel, :location)
87
+ end
88
+
89
+ it "should not detect unpreload association" do
90
+ Deal.includes({:hotel => :location}).each do |deal|
91
+ deal.hotel.location.name
92
+ end
93
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
+ # This test is just used for http://github.com/flyerhzm/bullet/issues#issue/20
5
+ describe Bullet::Detector::Association do
6
+
7
+ describe "for peschkaj" do
8
+ def setup_db
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+ create_table :categories do |t|
11
+ t.column :name, :string
12
+ end
13
+
14
+ create_table :submissions do |t|
15
+ t.column :name, :string
16
+ t.column :category_id, :integer
17
+ t.column :user_id, :integer
18
+ end
19
+
20
+ create_table :users do |t|
21
+ t.column :name, :string
22
+ t.column :category_id, :integer
23
+ end
24
+ end
25
+ end
26
+
27
+ def teardown_db
28
+ ActiveRecord::Base.connection.tables.each do |table|
29
+ ActiveRecord::Base.connection.drop_table(table)
30
+ end
31
+ end
32
+
33
+ class Category < ActiveRecord::Base
34
+ has_many :submissions
35
+ has_many :users
36
+ end
37
+
38
+ class Submission < ActiveRecord::Base
39
+ belongs_to :category
40
+ belongs_to :user
41
+ end
42
+
43
+ class User < ActiveRecord::Base
44
+ has_one :submission
45
+ belongs_to :category
46
+ end
47
+
48
+ before(:all) do
49
+ setup_db
50
+
51
+ category1 = Category.create(:name => "category1")
52
+ category2 = Category.create(:name => "category2")
53
+
54
+ user1 = User.create(:name => 'user1', :category => category1)
55
+ user2 = User.create(:name => 'user2', :category => category1)
56
+
57
+ submission1 = category1.submissions.create(:name => "submission1", :user => user1)
58
+ submission2 = category1.submissions.create(:name => "submission2", :user => user2)
59
+ submission3 = category2.submissions.create(:name => "submission3", :user => user1)
60
+ submission4 = category2.submissions.create(:name => "submission4", :user => user2)
61
+ end
62
+
63
+ after(:all) do
64
+ teardown_db
65
+ end
66
+
67
+ before(:each) do
68
+ Bullet.start_request
69
+ end
70
+
71
+ after(:each) do
72
+ Bullet.end_request
73
+ end
74
+
75
+ it "should not detect unused preload associations" do
76
+ category = Category.includes({:submissions => :user}).order("id DESC").find_by_name('category1')
77
+ category.submissions.map do |submission|
78
+ submission.name
79
+ submission.user.name
80
+ end
81
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
82
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Category, :submissions)
83
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Submission, :user)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,889 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
+
5
+ describe Bullet::Detector::Association, 'has_many' do
6
+
7
+ def setup_db
8
+ ActiveRecord::Schema.define(:version => 1) do
9
+ create_table :categories do |t|
10
+ t.column :name, :string
11
+ end
12
+
13
+ create_table :posts do |t|
14
+ t.column :name, :string
15
+ t.column :category_id, :integer
16
+ t.column :writer_id, :integer
17
+ end
18
+
19
+ create_table :comments do |t|
20
+ t.column :name, :string
21
+ t.column :post_id, :integer
22
+ t.column :author_id, :integer
23
+ end
24
+
25
+ create_table :entries do |t|
26
+ t.column :name, :string
27
+ t.column :category_id, :integer
28
+ end
29
+
30
+ create_table :base_users do |t|
31
+ t.column :name, :string
32
+ t.column :type, :string
33
+ t.column :newspaper_id, :integer
34
+ end
35
+ create_table :newspapers do |t|
36
+ t.column :name, :string
37
+ end
38
+ end
39
+ end
40
+
41
+ def teardown_db
42
+ ActiveRecord::Base.connection.tables.each do |table|
43
+ ActiveRecord::Base.connection.drop_table(table)
44
+ end
45
+ end
46
+
47
+ class Category < ActiveRecord::Base
48
+ has_many :posts
49
+ has_many :entries
50
+ end
51
+
52
+ class Post < ActiveRecord::Base
53
+ belongs_to :category
54
+ has_many :comments
55
+ belongs_to :writer
56
+
57
+
58
+ scope :preload_posts, lambda { includes(:comments) }
59
+ scope :in_category_name, lambda { |name|
60
+ where(['categories.name = ?', name]).includes(:category)
61
+ }
62
+ end
63
+
64
+ class Entry < ActiveRecord::Base
65
+ belongs_to :category
66
+ end
67
+
68
+ class Comment < ActiveRecord::Base
69
+ belongs_to :post
70
+ belongs_to :author, :class_name => "BaseUser"
71
+ end
72
+
73
+ class BaseUser < ActiveRecord::Base
74
+ has_many :comments
75
+ has_many :posts
76
+ belongs_to :newspaper
77
+ end
78
+
79
+ class Newspaper < ActiveRecord::Base
80
+ has_many :writers, :class_name => "BaseUser"
81
+ end
82
+
83
+ class Writer < BaseUser
84
+ end
85
+
86
+ before(:all) do
87
+ setup_db
88
+
89
+ newspaper1 = Newspaper.create(:name => "First Newspaper")
90
+ newspaper2 = Newspaper.create(:name => "Second Newspaper")
91
+
92
+ writer1 = Writer.create(:name => 'first', :newspaper => newspaper1)
93
+ writer2 = Writer.create(:name => 'second', :newspaper => newspaper2)
94
+ user1 = BaseUser.create(:name => 'third', :newspaper => newspaper1)
95
+ user2 = BaseUser.create(:name => 'fourth', :newspaper => newspaper2)
96
+
97
+
98
+ category1 = Category.create(:name => 'first')
99
+ category2 = Category.create(:name => 'second')
100
+
101
+ post1 = category1.posts.create(:name => 'first', :writer => writer1)
102
+ post1a = category1.posts.create(:name => 'like first', :writer => writer2)
103
+ post2 = category2.posts.create(:name => 'second', :writer => writer2)
104
+
105
+ comment1 = post1.comments.create(:name => 'first', :author => writer1)
106
+ comment2 = post1.comments.create(:name => 'first2', :author => writer1)
107
+ comment3 = post1.comments.create(:name => 'first3', :author => writer1)
108
+ comment4 = post1.comments.create(:name => 'second', :author => writer2)
109
+ comment8 = post1a.comments.create(:name => "like first 1", :author => writer1)
110
+ comment9 = post1a.comments.create(:name => "like first 2", :author => writer2)
111
+ comment5 = post2.comments.create(:name => 'third', :author => user1)
112
+ comment6 = post2.comments.create(:name => 'fourth', :author => user2)
113
+ comment7 = post2.comments.create(:name => 'fourth', :author => writer1)
114
+
115
+ entry1 = category1.entries.create(:name => 'first')
116
+ entry2 = category1.entries.create(:name => 'second')
117
+ end
118
+
119
+ after(:all) do
120
+ teardown_db
121
+ end
122
+
123
+ before(:each) do
124
+ Bullet.start_request
125
+ end
126
+
127
+ after(:each) do
128
+ Bullet.end_request
129
+ end
130
+
131
+ context "for unused cases" do
132
+ #If you have the same record created twice with different includes
133
+ # the hash value get's accumulated includes, which leads to false Unused eager loading
134
+ #it "should not incorrectly mark associations as unused when multiple object instances" do
135
+ #comments_with_author = Comment.includes(:author)
136
+ #comments_with_post = Comment.includes(:post)
137
+ #comments_with_author.each { |c| c.author.name }
138
+ #comments_with_author.each { |c| c.post.name }
139
+ #Bullet::Association.check_unused_preload_associations
140
+ #Bullet::Association.should be_unused_preload_associations_for(Comment, :post)
141
+ #Bullet::Association.should be_detecting_unpreloaded_association_for(Comment, :post)
142
+ #end
143
+
144
+ # same as above with different Models being queried
145
+ #it "should not incorrectly mark associations as unused when multiple object instances different Model" do
146
+ #post_with_comments = Post.includes(:comments)
147
+ #comments_with_author = Comment.includes(:author)
148
+ #post_with_comments.each { |p| p.comments.first.author.name }
149
+ #comments_with_author.each { |c| c.name }
150
+ #Bullet::Association.check_unused_preload_associations
151
+ #Bullet::Association.should be_unused_preload_associations_for(Comment, :author)
152
+ #Bullet::Association.should be_detecting_unpreloaded_association_for(Comment, :author)
153
+ #end
154
+
155
+ # this test passes right now. But is a regression test to ensure that if only a small set of returned records
156
+ # is not used that a unused preload association error is not generated
157
+ it "should not have unused when small set of returned records are discarded" do
158
+ comments_with_author = Comment.includes(:author)
159
+ comment_collection = comments_with_author.limit(2)
160
+ comment_collection.collect { |com| com.author.name }
161
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
162
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Comment, :author)
163
+ end
164
+ end
165
+
166
+
167
+ context "comments => posts => category" do
168
+
169
+ # this happens because the post isn't a possible object even though the writer is access through the post
170
+ # which leads to an 1+N queries
171
+ it "should detect unpreloaded writer" do
172
+ Comment.includes([:author, :post]).where(["base_users.id = ?", BaseUser.first]).each do |com|
173
+ com.post.writer.name
174
+ end
175
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Post, :writer)
176
+ end
177
+
178
+ # this happens because the comment doesn't break down the hash into keys
179
+ # properly creating an association from comment to post
180
+ it "should detect preload of comment => post" do
181
+ comments = Comment.includes([:author, {:post => :writer}]).where(["base_users.id = ?", BaseUser.first]).each do |com|
182
+ com.post.writer.name
183
+ end
184
+ Bullet::Detector::Association.should_not be_detecting_unpreloaded_association_for(Comment, :post)
185
+ Bullet::Detector::Association.should be_completely_preloading_associations
186
+ end
187
+
188
+ it "should detect preload of post => writer" do
189
+ comments = Comment.includes([:author, {:post => :writer}]).where(["base_users.id = ?", BaseUser.first]).each do |com|
190
+ com.post.writer.name
191
+ end
192
+ Bullet::Detector::Association.should be_creating_object_association_for(comments.first, :author)
193
+ Bullet::Detector::Association.should_not be_detecting_unpreloaded_association_for(Post, :writer)
194
+ Bullet::Detector::Association.should be_completely_preloading_associations
195
+ end
196
+
197
+ # To flyerhzm: This does not detect that newspaper is unpreloaded. The association is
198
+ # not within possible objects, and thus cannot be detected as unpreloaded
199
+ it "should detect unpreloading of writer => newspaper" do
200
+ comments = Comment.all(:include => {:post => :writer}, :conditions => "posts.name like '%first%'").each do |com|
201
+ com.post.writer.newspaper.name
202
+ end
203
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Writer, :newspaper)
204
+ end
205
+
206
+ # when we attempt to access category, there is an infinite overflow because load_target is hijacked leading to
207
+ # a repeating loop of calls in this test
208
+ it "should not raise a stack error from posts to category" do
209
+ lambda {
210
+ Comment.includes({:post => :category}).each do |com|
211
+ com.post.category
212
+ end
213
+ }.should_not raise_error(SystemStackError)
214
+ end
215
+ end
216
+
217
+ context "post => comments" do
218
+ #
219
+ ### FIXME: Please double check semantic equivalence with original
220
+ it "should detect preload with post => comments" do
221
+ Post.includes(:comments).each do |post|
222
+ post.comments.collect(&:name)
223
+ end
224
+ # Bullet::Detector::Association.should_not be_has_unpreload_associations
225
+ Bullet::Detector::Association.should be_completely_preloading_associations
226
+ end
227
+
228
+ it "should detect no preload post => comments" do
229
+ Post.all.each do |post|
230
+ post.comments.collect(&:name)
231
+ end
232
+ # Bullet::Detector::Association.should be_has_unpreload_associations
233
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
234
+ end
235
+
236
+ it "should detect unused preload post => comments for post" do
237
+ Post.includes(:comments).collect(&:name)
238
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
239
+ Bullet::Detector::Association.should be_has_unused_preload_associations
240
+ end
241
+
242
+ it "should detect no unused preload post => comments for post" do
243
+ Post.all.collect(&:name)
244
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
245
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
246
+ end
247
+
248
+ it "should detect no unused preload post => comments for comment" do
249
+ Post.all.each do |post|
250
+ post.comments.collect(&:name)
251
+ end
252
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
253
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
254
+
255
+ Bullet.end_request
256
+ Bullet.start_request
257
+
258
+ Post.all.each do |post|
259
+ post.comments.collect(&:name)
260
+ end
261
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
262
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
263
+ end
264
+ end
265
+
266
+ context "category => posts => comments" do
267
+ it "should detect preload with category => posts => comments" do
268
+ Category.includes({:posts => :comments}).each do |category|
269
+ category.posts.each do |post|
270
+ post.comments.collect(&:name)
271
+ end
272
+ end
273
+ # Bullet::Detector::Association.should_not be_has_unpreload_associations
274
+ Bullet::Detector::Association.should be_completely_preloading_associations
275
+ end
276
+
277
+ it "should detect preload category => posts, but no post => comments" do
278
+ Category.includes(:posts).each do |category|
279
+ category.posts.each do |post|
280
+ post.comments.collect(&:name)
281
+ end
282
+ end
283
+ # Bullet::Detector::Association.should be_has_unpreload_associations
284
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
285
+ end
286
+
287
+ it "should detect no preload category => posts => comments" do
288
+ Category.all.each do |category|
289
+ category.posts.each do |post|
290
+ post.comments.collect(&:name)
291
+ end
292
+ end
293
+ # Bullet::Detector::Association.should be_has_unpreload_associations
294
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
295
+ end
296
+
297
+ it "should detect unused preload with category => posts => comments" do
298
+ Category.includes({:posts => :comments}).collect(&:name)
299
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
300
+ Bullet::Detector::Association.should be_has_unused_preload_associations
301
+ end
302
+
303
+ it "should detect unused preload with post => commnets, no category => posts" do
304
+ Category.includes({:posts => :comments}).each do |category|
305
+ category.posts.collect(&:name)
306
+ end
307
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
308
+ Bullet::Detector::Association.should be_has_unused_preload_associations
309
+ end
310
+
311
+ it "should no detect preload with category => posts => comments" do
312
+ Category.all.each do |category|
313
+ category.posts.each do |post|
314
+ post.comments.collect(&:name)
315
+ end
316
+ end
317
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
318
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
319
+ end
320
+ end
321
+
322
+ context "category => posts, category => entries" do
323
+ it "should detect preload with category => [posts, entries]" do
324
+ Category.includes([:posts, :entries]).each do |category|
325
+ category.posts.collect(&:name)
326
+ category.entries.collect(&:name)
327
+ end
328
+ Bullet::Detector::Association.should be_completely_preloading_associations
329
+ end
330
+
331
+ it "should detect preload with category => posts, but no category => entries" do
332
+ Category.includes(:posts).each do |category|
333
+ category.posts.collect(&:name)
334
+ category.entries.collect(&:name)
335
+ end
336
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
337
+ end
338
+
339
+ it "should detect no preload with category => [posts, entries]" do
340
+ Category.all.each do |category|
341
+ category.posts.collect(&:name)
342
+ category.entries.collect(&:name)
343
+ end
344
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
345
+ end
346
+
347
+ it "should detect unused with category => [posts, entries]" do
348
+ Category.includes([:posts, :entries]).collect(&:name)
349
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
350
+ Bullet::Detector::Association.should be_has_unused_preload_associations
351
+ end
352
+
353
+ it "should detect unused preload with category => entries, but no category => posts" do
354
+ Category.includes([:posts, :entries]).each do |category|
355
+ category.posts.collect(&:name)
356
+ end
357
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
358
+ Bullet::Detector::Association.should be_has_unused_preload_associations
359
+ end
360
+
361
+ it "should detect no unused preload" do
362
+ Category.all.each do |category|
363
+ category.posts.collect(&:name)
364
+ category.entries.collect(&:name)
365
+ end
366
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
367
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
368
+ end
369
+ end
370
+
371
+ context "no preload" do
372
+ it "should no preload only display only one post => comment" do
373
+ Post.includes(:comments).each do |post|
374
+ post.comments.first.name
375
+ end
376
+ Bullet::Detector::Association.should be_completely_preloading_associations
377
+ end
378
+
379
+ it "should no preload only one post => commnets" do
380
+ Post.first.comments.collect(&:name)
381
+ Bullet::Detector::Association.should be_completely_preloading_associations
382
+ end
383
+ end
384
+
385
+ context "scope for_category_name" do
386
+ it "should detect preload with post => category" do
387
+ Post.in_category_name('first').all.each do |post|
388
+ post.category.name
389
+ end
390
+ Bullet::Detector::Association.should be_completely_preloading_associations
391
+ end
392
+
393
+ it "should not be unused preload post => category" do
394
+ Post.in_category_name('first').all.collect(&:name)
395
+ Bullet::Detector::Association.should be_completely_preloading_associations
396
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
397
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
398
+ end
399
+ end
400
+
401
+ context "scope preload_posts" do
402
+ it "should no preload post => comments with scope" do
403
+ Post.preload_posts.each do |post|
404
+ post.comments.collect(&:name)
405
+ end
406
+ Bullet::Detector::Association.should be_completely_preloading_associations
407
+ end
408
+
409
+ it "should unused preload with scope" do
410
+ Post.preload_posts.collect(&:name)
411
+ Bullet::Detector::Association.should be_completely_preloading_associations
412
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
413
+ Bullet::Detector::Association.should be_has_unused_preload_associations
414
+ end
415
+ end
416
+
417
+ context "no unused" do
418
+ it "should no unused only display only one post => comment" do
419
+ Post.includes(:comments).each do |post|
420
+ i = 0
421
+ post.comments.each do |comment|
422
+ if i == 0
423
+ comment.name
424
+ else
425
+ i += 1
426
+ end
427
+ end
428
+ end
429
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
430
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
431
+ end
432
+ end
433
+
434
+ context "belongs_to" do
435
+ it "should preload comments => post" do
436
+ Comment.all.each do |comment|
437
+ comment.post.name
438
+ end
439
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
440
+ end
441
+
442
+ it "should no preload comment => post" do
443
+ Comment.first.post.name
444
+ Bullet::Detector::Association.should be_completely_preloading_associations
445
+ end
446
+
447
+ it "should no preload comments => post" do
448
+ Comment.includes(:post).each do |comment|
449
+ comment.post.name
450
+ end
451
+ Bullet::Detector::Association.should be_completely_preloading_associations
452
+ end
453
+
454
+ it "should detect no unused preload comments => post" do
455
+ Comment.all.collect(&:name)
456
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
457
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
458
+ end
459
+
460
+ it "should detect unused preload comments => post" do
461
+ Comment.includes(:post).collect(&:name)
462
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
463
+ Bullet::Detector::Association.should be_has_unused_preload_associations
464
+ end
465
+
466
+ it "should dectect no unused preload comments => post" do
467
+ Comment.all.each do |comment|
468
+ comment.post.name
469
+ end
470
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
471
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
472
+ end
473
+
474
+ it "should dectect no unused preload comments => post" do
475
+ Comment.includes(:post).each do |comment|
476
+ comment.post.name
477
+ end
478
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
479
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
480
+ end
481
+ end
482
+ end
483
+
484
+ describe Bullet::Detector::Association, 'has_and_belongs_to_many' do
485
+
486
+ def setup_db
487
+ ActiveRecord::Schema.define(:version => 1) do
488
+ create_table :students do |t|
489
+ t.column :name, :string
490
+ end
491
+
492
+ create_table :teachers do |t|
493
+ t.column :name, :string
494
+ end
495
+
496
+ create_table :students_teachers, :id => false do |t|
497
+ t.column :student_id, :integer
498
+ t.column :teacher_id, :integer
499
+ end
500
+ end
501
+ end
502
+
503
+ def teardown_db
504
+ ActiveRecord::Base.connection.tables.each do |table|
505
+ ActiveRecord::Base.connection.drop_table(table)
506
+ end
507
+ end
508
+
509
+ class Student < ActiveRecord::Base
510
+ has_and_belongs_to_many :teachers
511
+ end
512
+
513
+ class Teacher < ActiveRecord::Base
514
+ has_and_belongs_to_many :students
515
+ end
516
+
517
+ before(:all) do
518
+ setup_db
519
+ student1 = Student.create(:name => 'first')
520
+ student2 = Student.create(:name => 'second')
521
+ teacher1 = Teacher.create(:name => 'first')
522
+ teacher2 = Teacher.create(:name => 'second')
523
+ student1.teachers = [teacher1, teacher2]
524
+ student2.teachers = [teacher1, teacher2]
525
+ teacher1.students << student1
526
+ teacher2.students << student2
527
+ end
528
+
529
+ after(:all) do
530
+ teardown_db
531
+ end
532
+
533
+ before(:each) do
534
+ Bullet.start_request
535
+ end
536
+
537
+ after(:each) do
538
+ Bullet.end_request
539
+ end
540
+
541
+ it "should detect unpreload associations" do
542
+ Student.all.each do |student|
543
+ student.teachers.collect(&:name)
544
+ end
545
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
546
+ end
547
+
548
+ it "should detect no unpreload associations" do
549
+ Student.includes(:teachers).each do |student|
550
+ student.teachers.collect(&:name)
551
+ end
552
+ Bullet::Detector::Association.should be_completely_preloading_associations
553
+ end
554
+
555
+ it "should detect unused preload associations" do
556
+ Student.includes(:teachers).collect(&:name)
557
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
558
+ Bullet::Detector::Association.should be_has_unused_preload_associations
559
+ end
560
+
561
+ it "should detect no unused preload associations" do
562
+ Student.all.collect(&:name)
563
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
564
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
565
+ end
566
+ end
567
+
568
+ describe Bullet::Detector::Association, 'has_many :through' do
569
+
570
+ def setup_db
571
+ ActiveRecord::Schema.define(:version => 1) do
572
+ create_table :firms do |t|
573
+ t.column :name, :string
574
+ end
575
+
576
+ create_table :clients do |t|
577
+ t.column :name, :string
578
+ end
579
+
580
+ create_table :relationships do |t|
581
+ t.column :firm_id, :integer
582
+ t.column :client_id, :integer
583
+ end
584
+ end
585
+ end
586
+
587
+ def teardown_db
588
+ ActiveRecord::Base.connection.tables.each do |table|
589
+ ActiveRecord::Base.connection.drop_table(table)
590
+ end
591
+ end
592
+
593
+ class Firm < ActiveRecord::Base
594
+ has_many :relationships
595
+ has_many :clients, :through => :relationships
596
+ end
597
+
598
+ class Client < ActiveRecord::Base
599
+ has_many :relationships
600
+ has_many :firms, :through => :relationships
601
+ end
602
+
603
+ class Relationship < ActiveRecord::Base
604
+ belongs_to :firm
605
+ belongs_to :client
606
+ end
607
+
608
+ before(:all) do
609
+ setup_db
610
+ firm1 = Firm.create(:name => 'first')
611
+ firm2 = Firm.create(:name => 'second')
612
+ client1 = Client.create(:name => 'first')
613
+ client2 = Client.create(:name => 'second')
614
+ firm1.clients = [client1, client2]
615
+ firm2.clients = [client1, client2]
616
+ client1.firms << firm1
617
+ client2.firms << firm2
618
+ end
619
+
620
+ after(:all) do
621
+ teardown_db
622
+ end
623
+
624
+ before(:each) do
625
+ Bullet.start_request
626
+ end
627
+
628
+ after(:each) do
629
+ Bullet.end_request
630
+ end
631
+
632
+ it "should detect unpreload associations" do
633
+ Firm.all.each do |firm|
634
+ firm.clients.collect(&:name)
635
+ end
636
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
637
+ end
638
+
639
+ it "should detect no unpreload associations" do
640
+ Firm.includes(:clients).each do |firm|
641
+ firm.clients.collect(&:name)
642
+ end
643
+ Bullet::Detector::Association.should be_completely_preloading_associations
644
+ end
645
+
646
+ it "should detect no unused preload associations" do
647
+ Firm.all.collect(&:name)
648
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
649
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
650
+ end
651
+
652
+ it "should detect unused preload associations" do
653
+ Firm.includes(:clients).collect(&:name)
654
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
655
+ Bullet::Detector::Association.should be_has_unused_preload_associations
656
+ end
657
+ end
658
+
659
+
660
+
661
+ describe Bullet::Detector::Association, "has_one" do
662
+
663
+ def setup_db
664
+ ActiveRecord::Schema.define(:version => 1) do
665
+ create_table :companies do |t|
666
+ t.column :name, :string
667
+ end
668
+
669
+ create_table :addresses do |t|
670
+ t.column :name, :string
671
+ t.column :company_id, :integer
672
+ end
673
+ end
674
+ end
675
+
676
+ def teardown_db
677
+ ActiveRecord::Base.connection.tables.each do |table|
678
+ ActiveRecord::Base.connection.drop_table(table)
679
+ end
680
+ end
681
+
682
+ class Company < ActiveRecord::Base
683
+ has_one :address
684
+ end
685
+
686
+ class Address < ActiveRecord::Base
687
+ belongs_to :company
688
+ end
689
+
690
+ before(:all) do
691
+ setup_db
692
+
693
+ company1 = Company.create(:name => 'first')
694
+ company2 = Company.create(:name => 'second')
695
+
696
+ Address.create(:name => 'first', :company => company1)
697
+ Address.create(:name => 'second', :company => company2)
698
+ end
699
+
700
+ after(:all) do
701
+ teardown_db
702
+ end
703
+
704
+ before(:each) do
705
+ Bullet.start_request
706
+ end
707
+
708
+ after(:each) do
709
+ Bullet.end_request
710
+ end
711
+
712
+ it "should detect unpreload association" do
713
+ Company.all.each do |company|
714
+ company.address.name
715
+ end
716
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
717
+ end
718
+
719
+ it "should detect no unpreload association" do
720
+ Company.find(:all, :include => :address).each do |company|
721
+ company.address.name
722
+ end
723
+ Bullet::Detector::Association.should be_completely_preloading_associations
724
+ end
725
+
726
+ it "should detect no unused preload association" do
727
+ Company.all.collect(&:name)
728
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
729
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
730
+ end
731
+
732
+ it "should detect unused preload association" do
733
+ Company.find(:all, :include => :address).collect(&:name)
734
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
735
+ Bullet::Detector::Association.should be_has_unused_preload_associations
736
+ end
737
+ end
738
+
739
+ describe Bullet::Detector::Association, "call one association that in possible objects" do
740
+
741
+ def setup_db
742
+ ActiveRecord::Schema.define(:version => 1) do
743
+ create_table :contacts do |t|
744
+ t.column :name, :string
745
+ end
746
+
747
+ create_table :emails do |t|
748
+ t.column :name, :string
749
+ t.column :contact_id, :integer
750
+ end
751
+ end
752
+ end
753
+
754
+ def teardown_db
755
+ ActiveRecord::Base.connection.tables.each do |table|
756
+ ActiveRecord::Base.connection.drop_table(table)
757
+ end
758
+ end
759
+
760
+ class Contact < ActiveRecord::Base
761
+ has_many :emails
762
+ end
763
+
764
+ class Email < ActiveRecord::Base
765
+ belongs_to :contact
766
+ end
767
+
768
+ before(:all) do
769
+ setup_db
770
+
771
+ contact1 = Contact.create(:name => 'first')
772
+ contact2 = Contact.create(:name => 'second')
773
+
774
+ email1 = contact1.emails.create(:name => 'first')
775
+ email2 = contact1.emails.create(:name => 'second')
776
+ email3 = contact2.emails.create(:name => 'third')
777
+ email4 = contact2.emails.create(:name => 'fourth')
778
+ end
779
+
780
+ after(:all) do
781
+ teardown_db
782
+ end
783
+
784
+ before(:each) do
785
+ Bullet.start_request
786
+ end
787
+
788
+ after(:each) do
789
+ Bullet.end_request
790
+ end
791
+
792
+ it "should detect no unpreload association" do
793
+ Contact.all
794
+ Contact.first.emails.collect(&:name)
795
+ Bullet::Detector::Association.should be_completely_preloading_associations
796
+ end
797
+ end
798
+
799
+ describe Bullet::Detector::Association, "STI" do
800
+
801
+ def setup_db
802
+ ActiveRecord::Schema.define(:version => 1) do
803
+ create_table :documents do |t|
804
+ t.string :name
805
+ t.string :type
806
+ t.integer :parent_id
807
+ t.integer :author_id
808
+ end
809
+
810
+ create_table :authors do |t|
811
+ t.string :name
812
+ end
813
+ end
814
+ end
815
+
816
+ def teardown_db
817
+ ActiveRecord::Base.connection.tables.each do |table|
818
+ ActiveRecord::Base.connection.drop_table(table)
819
+ end
820
+ end
821
+
822
+ class Document < ActiveRecord::Base
823
+ has_many :children, :class_name => "Document", :foreign_key => 'parent_id'
824
+ belongs_to :parent, :class_name => "Document", :foreign_key => 'parent_id'
825
+ belongs_to :author
826
+ end
827
+
828
+ class Page < Document
829
+ end
830
+
831
+ class Folder < Document
832
+ end
833
+
834
+ class Author < ActiveRecord::Base
835
+ has_many :documents
836
+ end
837
+
838
+ before(:all) do
839
+ setup_db
840
+ author1 = Author.create(:name => 'author1')
841
+ author2 = Author.create(:name => 'author2')
842
+ folder1 = Folder.create(:name => 'folder1', :author_id => author1.id)
843
+ folder2 = Folder.create(:name => 'folder2', :author_id => author2.id)
844
+ page1 = Page.create(:name => 'page1', :parent_id => folder1.id, :author_id => author1.id)
845
+ page2 = Page.create(:name => 'page2', :parent_id => folder1.id, :author_id => author1.id)
846
+ page3 = Page.create(:name => 'page3', :parent_id => folder2.id, :author_id => author2.id)
847
+ page4 = Page.create(:name => 'page4', :parent_id => folder2.id, :author_id => author2.id)
848
+ end
849
+
850
+ before(:each) do
851
+ Bullet.start_request
852
+ end
853
+
854
+ after(:each) do
855
+ Bullet.end_request
856
+ end
857
+
858
+ it "should detect unpreload associations" do
859
+ Page.all.each do |page|
860
+ page.author.name
861
+ end
862
+ Bullet::Detector::Association.should_not be_completely_preloading_associations
863
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
864
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
865
+ end
866
+
867
+ it "should not detect unpreload associations" do
868
+ Page.find(:all, :include => :author).each do |page|
869
+ page.author.name
870
+ end
871
+ Bullet::Detector::Association.should be_completely_preloading_associations
872
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
873
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
874
+ end
875
+
876
+ it "should detect unused preload associations" do
877
+ Page.find(:all, :include => :author).collect(&:name)
878
+ Bullet::Detector::Association.should be_completely_preloading_associations
879
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
880
+ Bullet::Detector::Association.should be_has_unused_preload_associations
881
+ end
882
+
883
+ it "should not detect unused preload associations" do
884
+ Page.all.collect(&:name)
885
+ Bullet::Detector::Association.should be_completely_preloading_associations
886
+ Bullet::Detector::UnusedEagerAssociation.check_unused_preload_associations
887
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
888
+ end
889
+ end