bullet 4.6.0 → 4.7.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +10 -4
  6. data/Gemfile +4 -5
  7. data/Gemfile.lock +160 -0
  8. data/Gemfile.mongoid +15 -0
  9. data/Gemfile.mongoid-2.4.12 +15 -0
  10. data/Gemfile.mongoid-2.4.12.lock +163 -0
  11. data/Gemfile.mongoid-2.5.2 +15 -0
  12. data/Gemfile.mongoid-2.5.2.lock +163 -0
  13. data/Gemfile.mongoid-2.6.0 +15 -0
  14. data/Gemfile.mongoid-2.6.0.lock +163 -0
  15. data/Gemfile.mongoid-2.7.1 +15 -0
  16. data/Gemfile.mongoid-2.7.1.lock +163 -0
  17. data/Gemfile.mongoid-2.8.1 +15 -0
  18. data/Gemfile.mongoid-2.8.1.lock +166 -0
  19. data/Gemfile.mongoid-3.0.23 +15 -0
  20. data/Gemfile.mongoid-3.0.23.lock +163 -0
  21. data/Gemfile.mongoid-3.1.5 +15 -0
  22. data/Gemfile.mongoid-3.1.5.lock +163 -0
  23. data/Gemfile.mongoid.lock +167 -0
  24. data/Gemfile.rails-3.0.20 +3 -4
  25. data/Gemfile.rails-3.0.20.lock +147 -0
  26. data/Gemfile.rails-3.1.12 +14 -0
  27. data/Gemfile.rails-3.1.12.lock +157 -0
  28. data/Gemfile.rails-3.2.15 +14 -0
  29. data/Gemfile.rails-3.2.15.lock +155 -0
  30. data/Gemfile.rails-4.0.1 +14 -0
  31. data/Gemfile.rails-4.0.1.lock +150 -0
  32. data/README.md +16 -16
  33. data/bullet.gemspec +2 -1
  34. data/lib/bullet.rb +26 -2
  35. data/lib/bullet/dependency.rb +14 -23
  36. data/lib/bullet/mongoid4x.rb +55 -0
  37. data/lib/bullet/notification.rb +2 -0
  38. data/lib/bullet/notification/base.rb +4 -0
  39. data/lib/bullet/rack.rb +24 -3
  40. data/lib/bullet/version.rb +1 -1
  41. data/spec/bullet/detector/unused_eager_loading_spec.rb +1 -1
  42. data/spec/bullet/notification/base_spec.rb +2 -2
  43. data/spec/bullet/rack_spec.rb +9 -9
  44. data/spec/integration/{association_spec.rb → active_record3/association_spec.rb} +1 -1
  45. data/spec/integration/active_record4/association_spec.rb +698 -0
  46. data/spec/integration/mongoid/association_spec.rb +10 -7
  47. data/spec/models/client.rb +1 -1
  48. data/spec/models/comment.rb +2 -2
  49. data/spec/models/document.rb +2 -2
  50. data/spec/models/firm.rb +1 -1
  51. data/spec/models/mongoid/address.rb +2 -0
  52. data/spec/models/mongoid/category.rb +2 -0
  53. data/spec/models/mongoid/comment.rb +2 -0
  54. data/spec/models/mongoid/company.rb +2 -0
  55. data/spec/models/mongoid/entry.rb +2 -0
  56. data/spec/models/mongoid/post.rb +2 -0
  57. data/spec/models/mongoid/user.rb +2 -0
  58. data/spec/models/newspaper.rb +1 -1
  59. data/spec/models/pet.rb +1 -1
  60. data/spec/models/post.rb +3 -5
  61. data/spec/spec_helper.rb +21 -0
  62. data/spec/support/mongo_seed.rb +13 -1
  63. data/test.sh +12 -4
  64. metadata +49 -11
  65. data/.rvmrc +0 -2
  66. data/.rvmrc.example +0 -2
  67. data/Gemfile.rails-3.1.11 +0 -16
  68. data/Gemfile.rails-3.2.12 +0 -16
  69. data/Gemfile.rails-4-beta +0 -14
@@ -4,5 +4,7 @@ module Bullet
4
4
  autoload :UnusedEagerLoading, 'bullet/notification/unused_eager_loading'
5
5
  autoload :NPlusOneQuery, 'bullet/notification/n_plus_one_query'
6
6
  autoload :CounterCache, 'bullet/notification/counter_cache'
7
+
8
+ class UnoptimizedQueryError < StandardError; end
7
9
  end
8
10
  end
@@ -47,6 +47,10 @@ module Bullet
47
47
  self.notifier.out_of_channel_notify(self.full_notice)
48
48
  end
49
49
 
50
+ def short_notice
51
+ [whoami, url, title, body].compact.join("\n")
52
+ end
53
+
50
54
  def eql?(other)
51
55
  klazz_associations_str == other.klazz_associations_str
52
56
  end
@@ -8,7 +8,6 @@ module Bullet
8
8
 
9
9
  def call(env)
10
10
  return @app.call(env) unless Bullet.enable?
11
-
12
11
  Bullet.start_request
13
12
  status, headers, response = @app.call(env)
14
13
  return [status, headers, response] if file?(headers) || empty?(response)
@@ -17,6 +16,7 @@ module Bullet
17
16
  if Bullet.notification?
18
17
  if status == 200 && !response_body(response).frozen? && html_request?(headers, response)
19
18
  response_body = response_body(response) << Bullet.gather_inline_notifications
19
+ add_footer_note(response_body) if Bullet.add_footer
20
20
  headers['Content-Length'] = response_body.bytesize.to_s
21
21
  end
22
22
  Bullet.perform_out_of_channel_notifications(env)
@@ -34,10 +34,15 @@ module Bullet
34
34
  !response_body(response).respond_to?(:empty?) ||
35
35
  response_body(response).empty?
36
36
  else
37
- response_body(response).empty?
37
+ body = response_body(response)
38
+ body.nil? || body.empty?
38
39
  end
39
40
  end
40
41
 
42
+ def add_footer_note(response_body)
43
+ response_body << "<div #{footer_div_style}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
44
+ end
45
+
41
46
  # if send file?
42
47
  def file?(headers)
43
48
  headers["Content-Transfer-Encoding"] == "binary"
@@ -48,7 +53,23 @@ module Bullet
48
53
  end
49
54
 
50
55
  def response_body(response)
51
- rails? ? response.body : response.first
56
+ if rails?
57
+ Array === response.body ? response.body.first : response.body
58
+ else
59
+ response.first
60
+ end
61
+ end
62
+
63
+ private
64
+ def footer_div_style
65
+ <<EOF
66
+ style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
67
+ -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
68
+ -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
69
+ padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
70
+ color: rgb(119, 119, 119); font-size: 18px;"
71
+ EOF
52
72
  end
53
73
  end
54
74
  end
75
+
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "4.6.0"
3
+ VERSION = "4.7.0"
4
4
  end
@@ -10,7 +10,7 @@ module Bullet
10
10
  before(:each) { UnusedEagerLoading.clear }
11
11
 
12
12
  context ".call_associations" do
13
- it "should get empty array if eager_loadgins" do
13
+ it "should get empty array if eager_loadings" do
14
14
  UnusedEagerLoading.send(:call_associations, @post.bullet_ar_key, Set.new([:association])).should be_empty
15
15
  end
16
16
 
@@ -47,7 +47,7 @@ module Bullet
47
47
 
48
48
  context "#notify_inline" do
49
49
  it "should send full_notice to notifier" do
50
- notifier = stub
50
+ notifier = double
51
51
  subject.stub(:notifier => notifier, :full_notice => "full_notice")
52
52
  notifier.should_receive(:inline_notify).with("full_notice")
53
53
  subject.notify_inline
@@ -56,7 +56,7 @@ module Bullet
56
56
 
57
57
  context "#notify_out_of_channel" do
58
58
  it "should send full_out_of_channel to notifier" do
59
- notifier = stub
59
+ notifier = double
60
60
  subject.stub(:notifier => notifier, :full_notice => "full_notice")
61
61
  notifier.should_receive(:out_of_channel_notify).with("full_notice")
62
62
  subject.notify_out_of_channel
@@ -9,48 +9,48 @@ module Bullet
9
9
  context "#html_request?" do
10
10
  it "should be true if Content-Type is text/html and http body contains html tag" do
11
11
  headers = {"Content-Type" => "text/html"}
12
- response = stub(:body => "<html><head></head><body></body></html>")
12
+ response = double(:body => "<html><head></head><body></body></html>")
13
13
  middleware.should be_html_request(headers, response)
14
14
  end
15
15
 
16
16
  it "should be true if Content-Type is text/html and http body contains html tag with attributes" do
17
17
  headers = {"Content-Type" => "text/html"}
18
- response = stub(:body => "<html attr='hello'><head></head><body></body></html>")
18
+ response = double(:body => "<html attr='hello'><head></head><body></body></html>")
19
19
  middleware.should be_html_request(headers, response)
20
20
  end
21
21
 
22
22
  it "should be false if there is no Content-Type header" do
23
23
  headers = {}
24
- response = stub(:body => "<html><head></head><body></body></html>")
24
+ response = double(:body => "<html><head></head><body></body></html>")
25
25
  middleware.should_not be_html_request(headers, response)
26
26
  end
27
27
 
28
28
  it "should be false if Content-Type is javascript" do
29
29
  headers = {"Content-Type" => "text/javascript"}
30
- response = stub(:body => "<html><head></head><body></body></html>")
30
+ response = double(:body => "<html><head></head><body></body></html>")
31
31
  middleware.should_not be_html_request(headers, response)
32
32
  end
33
33
 
34
34
  it "should be false if response body doesn't contain html tag" do
35
35
  headers = {"Content-Type" => "text/html"}
36
- response = stub(:body => "<div>Partial</div>")
36
+ response = double(:body => "<div>Partial</div>")
37
37
  middleware.should_not be_html_request(headers, response)
38
38
  end
39
39
  end
40
40
 
41
41
  context "empty?" do
42
42
  it "should be false if response is a string and not empty" do
43
- response = stub(:body => "<html><head></head><body></body></html>")
43
+ response = double(:body => "<html><head></head><body></body></html>")
44
44
  middleware.should_not be_empty(response)
45
45
  end
46
46
 
47
- it "should be tru if response is not found" do
47
+ it "should be true if response is not found" do
48
48
  response = ["Not Found"]
49
49
  middleware.should be_empty(response)
50
50
  end
51
51
 
52
52
  it "should be true if response body is empty" do
53
- response = stub(:body => "")
53
+ response = double(:body => "")
54
54
  middleware.should be_empty(response)
55
55
  end
56
56
  end
@@ -91,7 +91,7 @@ module Bullet
91
91
  end
92
92
 
93
93
  context "when Bullet is disabled" do
94
- before(:each) { Bullet.stub(:enable?, false) }
94
+ before(:each) { Bullet.stub(:enable? => false) }
95
95
 
96
96
  it "should not call Bullet.start_request" do
97
97
  Bullet.should_not_receive(:start_request)
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- if active_record3? || active_record4?
3
+ if active_record3?
4
4
  describe Bullet::Detector::Association, 'has_many' do
5
5
  before(:each) do
6
6
  Bullet.clear
@@ -0,0 +1,698 @@
1
+ require 'spec_helper'
2
+
3
+ if active_record4?
4
+ describe Bullet::Detector::Association, 'has_many' do
5
+ before(:each) do
6
+ Bullet.clear
7
+ Bullet.start_request
8
+ end
9
+
10
+ after(:each) do
11
+ Bullet.end_request
12
+ end
13
+
14
+ context "post => comments" do
15
+ it "should detect non preload post => comments" do
16
+ Post.all.each do |post|
17
+ post.comments.map(&:name)
18
+ end
19
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
20
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
21
+
22
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Post, :comments)
23
+ end
24
+
25
+ it "should detect preload with post => comments" do
26
+ Post.includes(:comments).each do |post|
27
+ post.comments.map(&:name)
28
+ end
29
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
30
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
31
+
32
+ Bullet::Detector::Association.should be_completely_preloading_associations
33
+ end
34
+
35
+ it "should detect unused preload post => comments" do
36
+ Post.includes(:comments).map(&:name)
37
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
38
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Post, :comments)
39
+
40
+ Bullet::Detector::Association.should be_completely_preloading_associations
41
+ end
42
+
43
+ it "should not detect unused preload post => comments" do
44
+ Post.all.map(&:name)
45
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
46
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
47
+
48
+ Bullet::Detector::Association.should be_completely_preloading_associations
49
+ end
50
+
51
+ it "should detect non preload comment => post with inverse_of" do
52
+ Post.includes(:comments).each do |post|
53
+ post.comments.each do |comment|
54
+ comment.name
55
+ comment.post.name
56
+ end
57
+ end
58
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
59
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
60
+
61
+ Bullet::Detector::Association.should be_completely_preloading_associations
62
+ end
63
+ end
64
+
65
+ context "category => posts => comments" do
66
+ it "should detect non preload category => posts => comments" do
67
+ Category.all.each do |category|
68
+ category.posts.each do |post|
69
+ post.comments.map(&:name)
70
+ end
71
+ end
72
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
73
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
74
+
75
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Category, :posts)
76
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Post, :comments)
77
+ end
78
+
79
+ it "should detect preload category => posts, but no post => comments" do
80
+ Category.includes(:posts).each do |category|
81
+ category.posts.each do |post|
82
+ post.comments.map(&:name)
83
+ end
84
+ end
85
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
86
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
87
+
88
+ Bullet::Detector::Association.should_not be_detecting_unpreloaded_association_for(Category, :posts)
89
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Post, :comments)
90
+ end
91
+
92
+ it "should detect preload with category => posts => comments" do
93
+ Category.includes({:posts => :comments}).each do |category|
94
+ category.posts.each do |post|
95
+ post.comments.map(&:name)
96
+ end
97
+ end
98
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
99
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
100
+
101
+ Bullet::Detector::Association.should be_completely_preloading_associations
102
+ end
103
+
104
+ it "should detect unused preload with category => posts => comments" do
105
+ Category.includes({:posts => :comments}).map(&:name)
106
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
107
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Post, :comments)
108
+
109
+ Bullet::Detector::Association.should be_completely_preloading_associations
110
+ end
111
+
112
+ it "should detect unused preload with post => commnets, no category => posts" do
113
+ Category.includes({:posts => :comments}).each do |category|
114
+ category.posts.map(&:name)
115
+ end
116
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
117
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Post, :comments)
118
+
119
+ Bullet::Detector::Association.should be_completely_preloading_associations
120
+ end
121
+ end
122
+
123
+ context "category => posts, category => entries" do
124
+ it "should detect non preload with category => [posts, entries]" do
125
+ Category.all.each do |category|
126
+ category.posts.map(&:name)
127
+ category.entries.map(&:name)
128
+ end
129
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
130
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
131
+
132
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Category, :posts)
133
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Category, :entries)
134
+ end
135
+
136
+ it "should detect preload with category => posts, but not with category => entries" do
137
+ Category.includes(:posts).each do |category|
138
+ category.posts.map(&:name)
139
+ category.entries.map(&:name)
140
+ end
141
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
142
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
143
+
144
+ Bullet::Detector::Association.should_not be_detecting_unpreloaded_association_for(Category, :posts)
145
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Category, :entries)
146
+ end
147
+
148
+ it "should detect preload with category => [posts, entries]" do
149
+ Category.includes([:posts, :entries]).each do |category|
150
+ category.posts.map(&:name)
151
+ category.entries.map(&:name)
152
+ end
153
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
154
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
155
+
156
+ Bullet::Detector::Association.should be_completely_preloading_associations
157
+ end
158
+
159
+ it "should detect unused preload with category => [posts, entries]" do
160
+ Category.includes([:posts, :entries]).map(&:name)
161
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
162
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Category, :posts)
163
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Category, :entries)
164
+
165
+ Bullet::Detector::Association.should be_completely_preloading_associations
166
+ end
167
+
168
+ it "should detect unused preload with category => entries, but not with category => posts" do
169
+ Category.includes([:posts, :entries]).each do |category|
170
+ category.posts.map(&:name)
171
+ end
172
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
173
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Category, :posts)
174
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Category, :entries)
175
+
176
+ Bullet::Detector::Association.should be_completely_preloading_associations
177
+ end
178
+ end
179
+
180
+ context "post => comment" do
181
+ it "should detect unused preload with post => comments" do
182
+ Post.includes(:comments).each do |post|
183
+ post.comments.first.name
184
+ end
185
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
186
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Post, :comments)
187
+
188
+ Bullet::Detector::Association.should be_completely_preloading_associations
189
+ end
190
+
191
+ it "should detect preload with post => commnets" do
192
+ Post.first.comments.map(&:name)
193
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
194
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
195
+
196
+ Bullet::Detector::Association.should be_completely_preloading_associations
197
+ end
198
+ end
199
+
200
+ context "category => posts => writer" do
201
+ it "should not detect unused preload associations" do
202
+ category = Category.includes({:posts => :writer}).order("id DESC").find_by_name('first')
203
+ category.posts.map do |post|
204
+ post.name
205
+ post.writer.name
206
+ end
207
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
208
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Category, :posts)
209
+ Bullet::Detector::Association.should_not be_unused_preload_associations_for(Post, :writer)
210
+ end
211
+ end
212
+
213
+ context "scope for_category_name" do
214
+ it "should detect preload with post => category" do
215
+ Post.in_category_name('first').references(:categories).each do |post|
216
+ post.category.name
217
+ end
218
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
219
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
220
+
221
+ Bullet::Detector::Association.should be_completely_preloading_associations
222
+ end
223
+
224
+ it "should not be unused preload post => category" do
225
+ Post.in_category_name('first').references(:categories).map(&:name)
226
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
227
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
228
+
229
+ Bullet::Detector::Association.should be_completely_preloading_associations
230
+ end
231
+ end
232
+
233
+ context "scope preload_comments" do
234
+ it "should detect preload post => comments with scope" do
235
+ Post.preload_comments.each do |post|
236
+ post.comments.map(&:name)
237
+ end
238
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
239
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
240
+
241
+ Bullet::Detector::Association.should be_completely_preloading_associations
242
+ end
243
+
244
+ it "should detect unused preload with scope" do
245
+ Post.preload_comments.map(&:name)
246
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
247
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Post, :comments)
248
+
249
+ Bullet::Detector::Association.should be_completely_preloading_associations
250
+ end
251
+ end
252
+ end
253
+
254
+ describe Bullet::Detector::Association, 'belongs_to' do
255
+ before(:each) do
256
+ Bullet.clear
257
+ Bullet.start_request
258
+ end
259
+
260
+ after(:each) do
261
+ Bullet.end_request
262
+ end
263
+
264
+ context "comment => post" do
265
+ it "should detect non preload with comment => post" do
266
+ Comment.all.each do |comment|
267
+ comment.post.name
268
+ end
269
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
270
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
271
+
272
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Comment, :post)
273
+ end
274
+
275
+ it "should detect preload with one comment => post" do
276
+ Comment.first.post.name
277
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
278
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
279
+
280
+ Bullet::Detector::Association.should be_completely_preloading_associations
281
+ end
282
+
283
+ it "should dtect preload with comment => post" do
284
+ Comment.includes(:post).each do |comment|
285
+ comment.post.name
286
+ end
287
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
288
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
289
+
290
+ Bullet::Detector::Association.should be_completely_preloading_associations
291
+ end
292
+
293
+ it "should not detect preload with comment => post" do
294
+ Comment.all.map(&:name)
295
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
296
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
297
+
298
+ Bullet::Detector::Association.should be_completely_preloading_associations
299
+ end
300
+
301
+ it "should detect unused preload with comments => post" do
302
+ Comment.includes(:post).map(&:name)
303
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
304
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Comment, :post)
305
+
306
+ Bullet::Detector::Association.should be_completely_preloading_associations
307
+ end
308
+ end
309
+
310
+ context "comment => post => category" do
311
+ it "should detect non preload association with comment => post" do
312
+ Comment.all.each do |comment|
313
+ comment.post.category.name
314
+ end
315
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
316
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
317
+
318
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Comment, :post)
319
+ end
320
+
321
+ it "should detect non preload association with post => category" do
322
+ Comment.includes(:post).each do |comment|
323
+ comment.post.category.name
324
+ end
325
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
326
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
327
+
328
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Post, :category)
329
+ end
330
+
331
+ it "should not detect unpreload association" do
332
+ Comment.includes(:post => :category).each do |comment|
333
+ comment.post.category.name
334
+ end
335
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
336
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
337
+
338
+ Bullet::Detector::Association.should be_completely_preloading_associations
339
+ end
340
+ end
341
+
342
+ context "comment => author, post => writer" do
343
+ # this happens because the post isn't a possible object even though the writer is access through the post
344
+ # which leads to an 1+N queries
345
+ it "should detect non preloaded writer" do
346
+ Comment.includes([:author, :post]).where(["base_users.id = ?", BaseUser.first]).references(:base_users).each do |comment|
347
+ comment.post.writer.name
348
+ end
349
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
350
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
351
+
352
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Post, :writer)
353
+ end
354
+
355
+ # this happens because the comment doesn't break down the hash into keys
356
+ # properly creating an association from comment to post
357
+ it "should detect unused preload with comment => author" do
358
+ Comment.includes([:author, {:post => :writer}]).where(["base_users.id = ?", BaseUser.first]).references(:base_users).each do |comment|
359
+ comment.post.writer.name
360
+ end
361
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
362
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Comment, :author)
363
+
364
+ Bullet::Detector::Association.should be_completely_preloading_associations
365
+ end
366
+
367
+ # To flyerhzm: This does not detect that newspaper is unpreloaded. The association is
368
+ # not within possible objects, and thus cannot be detected as unpreloaded
369
+ it "should detect non preloading with writer => newspaper" do
370
+ Comment.includes(:post => :writer).where("posts.name like '%first%'").references(:posts).each do |comment|
371
+ comment.post.writer.newspaper.name
372
+ end
373
+ #Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
374
+ #Bullet::Detector::Association.should_not be_has_unused_preload_associations
375
+
376
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Writer, :newspaper)
377
+ end
378
+
379
+ # when we attempt to access category, there is an infinite overflow because load_target is hijacked leading to
380
+ # a repeating loop of calls in this test
381
+ it "should not raise a stack error from posts to category" do
382
+ expect {
383
+ Comment.includes({:post => :category}).each do |com|
384
+ com.post.category
385
+ end
386
+ }.not_to raise_error()
387
+ end
388
+ end
389
+ end
390
+
391
+ describe Bullet::Detector::Association, 'has_and_belongs_to_many' do
392
+ before(:each) do
393
+ Bullet.clear
394
+ Bullet.start_request
395
+ end
396
+
397
+ after(:each) do
398
+ Bullet.end_request
399
+ end
400
+
401
+ context "students <=> teachers" do
402
+ it "should detect non preload associations" do
403
+ Student.all.each do |student|
404
+ student.teachers.map(&:name)
405
+ end
406
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
407
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
408
+
409
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Student, :teachers)
410
+ end
411
+
412
+ it "should detect preload associations" do
413
+ Student.includes(:teachers).each do |student|
414
+ student.teachers.map(&:name)
415
+ end
416
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
417
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
418
+
419
+ Bullet::Detector::Association.should be_completely_preloading_associations
420
+ end
421
+
422
+ it "should detect unused preload associations" do
423
+ Student.includes(:teachers).map(&:name)
424
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
425
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Student, :teachers)
426
+
427
+ Bullet::Detector::Association.should be_completely_preloading_associations
428
+ end
429
+
430
+ it "should detect no unused preload associations" do
431
+ Student.all.map(&:name)
432
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
433
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
434
+
435
+ Bullet::Detector::Association.should be_completely_preloading_associations
436
+ end
437
+ end
438
+ end
439
+
440
+ describe Bullet::Detector::Association, 'has_many :through' do
441
+ before(:each) do
442
+ Bullet.clear
443
+ Bullet.start_request
444
+ end
445
+
446
+ after(:each) do
447
+ Bullet.end_request
448
+ end
449
+
450
+ context "firm => clients" do
451
+ it "should detect non preload associations" do
452
+ Firm.all.each do |firm|
453
+ firm.clients.map(&:name)
454
+ end
455
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
456
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
457
+
458
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Firm, :clients)
459
+ end
460
+
461
+ it "should detect preload associations" do
462
+ Firm.includes(:clients).each do |firm|
463
+ firm.clients.map(&:name)
464
+ end
465
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
466
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
467
+
468
+ Bullet::Detector::Association.should be_completely_preloading_associations
469
+ end
470
+
471
+ it "should not detect preload associations" do
472
+ Firm.all.map(&:name)
473
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
474
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
475
+
476
+ Bullet::Detector::Association.should be_completely_preloading_associations
477
+ end
478
+
479
+ it "should detect unused preload associations" do
480
+ Firm.includes(:clients).map(&:name)
481
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
482
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Firm, :clients)
483
+
484
+ Bullet::Detector::Association.should be_completely_preloading_associations
485
+ end
486
+ end
487
+ end
488
+
489
+ describe Bullet::Detector::Association, "has_one" do
490
+ before(:each) do
491
+ Bullet.clear
492
+ Bullet.start_request
493
+ end
494
+
495
+ after(:each) do
496
+ Bullet.end_request
497
+ end
498
+
499
+ context "company => address" do
500
+ it "should detect non preload association" do
501
+ Company.all.each do |company|
502
+ company.address.name
503
+ end
504
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
505
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
506
+
507
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Company, :address)
508
+ end
509
+
510
+ it "should detect preload association" do
511
+ Company.includes(:address).each do |company|
512
+ company.address.name
513
+ end
514
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
515
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
516
+
517
+ Bullet::Detector::Association.should be_completely_preloading_associations
518
+ end
519
+
520
+ it "should not detect preload association" do
521
+ Company.all.map(&:name)
522
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
523
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
524
+
525
+ Bullet::Detector::Association.should be_completely_preloading_associations
526
+ end
527
+
528
+ it "should detect unused preload association" do
529
+ Company.includes(:address).map(&:name)
530
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
531
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Company, :address)
532
+
533
+ Bullet::Detector::Association.should be_completely_preloading_associations
534
+ end
535
+ end
536
+ end
537
+
538
+ describe Bullet::Detector::Association, "call one association that in possible objects" do
539
+ before(:each) do
540
+ Bullet.clear
541
+ Bullet.start_request
542
+ end
543
+
544
+ after(:each) do
545
+ Bullet.end_request
546
+ end
547
+
548
+ it "should not detect preload association" do
549
+ Post.all
550
+ Post.first.comments.map(&:name)
551
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
552
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
553
+
554
+ Bullet::Detector::Association.should be_completely_preloading_associations
555
+ end
556
+ end
557
+
558
+ describe Bullet::Detector::Association, "STI" do
559
+ before(:each) do
560
+ Bullet.clear
561
+ Bullet.start_request
562
+ end
563
+
564
+ after(:each) do
565
+ Bullet.end_request
566
+ end
567
+
568
+ context "page => author" do
569
+ it "should detect non preload associations" do
570
+ Page.all.each do |page|
571
+ page.author.name
572
+ end
573
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
574
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
575
+
576
+ Bullet::Detector::Association.should be_detecting_unpreloaded_association_for(Page, :author)
577
+ end
578
+
579
+ it "should detect preload associations" do
580
+ Page.includes(:author).each do |page|
581
+ page.author.name
582
+ end
583
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
584
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
585
+
586
+ Bullet::Detector::Association.should be_completely_preloading_associations
587
+ end
588
+
589
+ it "should detect unused preload associations" do
590
+ Page.includes(:author).map(&:name)
591
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
592
+ Bullet::Detector::Association.should be_unused_preload_associations_for(Page, :author)
593
+
594
+ Bullet::Detector::Association.should be_completely_preloading_associations
595
+ end
596
+
597
+ it "should not detect preload associations" do
598
+ Page.all.map(&:name)
599
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
600
+ Bullet::Detector::Association.should_not be_has_unused_preload_associations
601
+
602
+ Bullet::Detector::Association.should be_completely_preloading_associations
603
+ end
604
+ end
605
+
606
+ context "disable n plus one query" do
607
+ before { Bullet.n_plus_one_query_enable = false }
608
+ after { Bullet.n_plus_one_query_enable = true }
609
+
610
+ it "should not detect n plus one query" do
611
+ Post.all.each do |post|
612
+ post.comments.map(&:name)
613
+ end
614
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
615
+
616
+ expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Post, :comments)
617
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
618
+ end
619
+
620
+ it "should still detect unused eager loading" do
621
+ Post.includes(:comments).map(&:name)
622
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
623
+
624
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
625
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
626
+ end
627
+ end
628
+
629
+ context "disable unused eager loading" do
630
+ before { Bullet.unused_eager_loading_enable = false }
631
+ after { Bullet.unused_eager_loading_enable = true }
632
+
633
+ it "should not detect unused eager loading" do
634
+ Post.includes(:comments).map(&:name)
635
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
636
+
637
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
638
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
639
+ end
640
+
641
+ it "should still detect n plus one query" do
642
+ Post.all.each do |post|
643
+ post.comments.map(&:name)
644
+ end
645
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
646
+
647
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
648
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
649
+ end
650
+ end
651
+
652
+ context "whitelist n plus one query" do
653
+ before { Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments }
654
+ after { Bullet.reset_whitelist }
655
+
656
+ it "should not detect n plus one query" do
657
+ Post.all.each do |post|
658
+ post.comments.map(&:name)
659
+ end
660
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
661
+
662
+ expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(Post, :comments)
663
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
664
+ end
665
+
666
+ it "should still detect unused eager loading" do
667
+ Post.includes(:comments).map(&:name)
668
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
669
+
670
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
671
+ expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
672
+ end
673
+ end
674
+
675
+ context "whitelist unused eager loading" do
676
+ before { Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments }
677
+ after { Bullet.reset_whitelist }
678
+
679
+ it "should not detect unused eager loading" do
680
+ Post.includes(:comments).map(&:name)
681
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
682
+
683
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
684
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
685
+ end
686
+
687
+ it "should still detect n plus one query" do
688
+ Post.all.each do |post|
689
+ post.comments.map(&:name)
690
+ end
691
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
692
+
693
+ expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Post, :comments)
694
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
695
+ end
696
+ end
697
+ end
698
+ end