bullet 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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