bullet 4.6.0 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
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