bullet 4.11.0 → 4.12.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/Gemfile.mongoid-4.0 +1 -1
- data/Gemfile.rails-3.2 +1 -1
- data/Gemfile.rails-4.0 +1 -1
- data/Gemfile.rails-4.1 +1 -1
- data/README.md +52 -46
- data/bullet.gemspec +2 -2
- data/lib/bullet/active_record3.rb +1 -1
- data/lib/bullet/active_record3x.rb +1 -1
- data/lib/bullet/active_record4.rb +1 -11
- data/lib/bullet/active_record41.rb +1 -0
- data/lib/bullet/detector/association.rb +13 -7
- data/lib/bullet/detector/counter_cache.rb +11 -11
- data/lib/bullet/detector/n_plus_one_query.rb +28 -18
- data/lib/bullet/detector/unused_eager_loading.rb +16 -16
- data/lib/bullet/ext/object.rb +8 -4
- data/lib/bullet/notification/base.rb +5 -5
- data/lib/bullet/rack.rb +1 -1
- data/lib/bullet/registry/object.rb +4 -4
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +5 -2
- data/spec/bullet/detector/association_spec.rb +2 -2
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +27 -27
- data/spec/bullet/detector/unused_eager_loading_spec.rb +15 -15
- data/spec/bullet/ext/object_spec.rb +17 -3
- data/spec/bullet/notification/base_spec.rb +37 -0
- data/spec/bullet/registry/object_spec.rb +4 -4
- data/spec/bullet_spec.rb +2 -2
- data/spec/integration/active_record3/association_spec.rb +2 -2
- data/spec/integration/active_record4/association_spec.rb +1 -1
- data/spec/models/category.rb +1 -1
- data/spec/models/post.rb +1 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/support/bullet_ext.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b201e99b1db5ea0ae016eb415b8bdcb2141a0321
|
|
4
|
+
data.tar.gz: 072c423687b8482273549f30c2538edec5c3c03f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 624bfcc8a8f2044699d19e598729cba0bf858e054002b435a31cce643d1bc3834139dad14ea510ef573a31059ce13f4b5c1dd892404645cca8d77e47264db794
|
|
7
|
+
data.tar.gz: ffaf8125f6797fd85b42be44f645e835796d623bc8b656662b8bd7387ac3b92882d4f2daaf2c50c04aced38113d7c96fbd8b3bdf631bd532c0a02663e221e1d1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Next Release
|
|
2
2
|
|
|
3
|
-
## 4.
|
|
3
|
+
## 4.12.0
|
|
4
|
+
|
|
5
|
+
* Fix false n+1 queries caused by inversed objects.
|
|
6
|
+
* Replace .id with .primary_key_value
|
|
7
|
+
* Rename bullet_ar_key to bullet_key
|
|
8
|
+
* Fix rails sse detect
|
|
9
|
+
* Fix bullet using in test environment
|
|
10
|
+
* Memoize whoami
|
|
11
|
+
|
|
12
|
+
## 4.11.0 (06/24/2014)
|
|
4
13
|
|
|
5
14
|
* Support empty? call on ar associations
|
|
6
15
|
* Skip detecting if object is a new record
|
data/Gemfile.mongoid-4.0
CHANGED
data/Gemfile.rails-3.2
CHANGED
data/Gemfile.rails-4.0
CHANGED
data/Gemfile.rails-4.1
CHANGED
data/README.md
CHANGED
|
@@ -60,7 +60,7 @@ config.after_initialize do
|
|
|
60
60
|
end
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
The notifier of
|
|
63
|
+
The notifier of Bullet is a wrap of [uniform_notifier](https://github.com/flyerhzm/uniform_notifier)
|
|
64
64
|
|
|
65
65
|
The code above will enable all seven of the Bullet notification systems:
|
|
66
66
|
* `Bullet.enable`: enable Bullet gem, otherwise do nothing
|
|
@@ -70,25 +70,31 @@ The code above will enable all seven of the Bullet notification systems:
|
|
|
70
70
|
* `Bullet.airbrake`: add notifications to airbrake
|
|
71
71
|
* `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
|
|
72
72
|
* `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
|
|
73
|
-
* `Bullet.xmpp`: send XMPP/Jabber notifications to the receiver indicated. Note that the code will currently not handle the adding of contacts, so you will need to make both accounts indicated know each other manually before you will receive any notifications. If you restart the development server frequently, the 'coming online' sound for the
|
|
73
|
+
* `Bullet.xmpp`: send XMPP/Jabber notifications to the receiver indicated. Note that the code will currently not handle the adding of contacts, so you will need to make both accounts indicated know each other manually before you will receive any notifications. If you restart the development server frequently, the 'coming online' sound for the Bullet account may start to annoy - in this case set :show_online_status to false; you will still get notifications, but the Bullet account won't announce it's online status anymore.
|
|
74
74
|
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
|
75
75
|
* `Bullet.add_footer`: adds the details in the bottom left corner of the page
|
|
76
76
|
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
|
|
77
77
|
|
|
78
|
-
Bullet also allows you to disable
|
|
79
|
-
and counter_cache detectors respectively.
|
|
78
|
+
Bullet also allows you to disable any of its detectors.
|
|
80
79
|
|
|
81
80
|
```ruby
|
|
82
|
-
|
|
81
|
+
# Each of these settings defaults to true
|
|
82
|
+
|
|
83
|
+
# Detect N+1 queries
|
|
84
|
+
Bullet.n_plus_one_query_enable = false
|
|
85
|
+
|
|
86
|
+
# Detect eager-loaded associations which are not used
|
|
83
87
|
Bullet.unused_eager_loading_enable = false
|
|
84
|
-
|
|
88
|
+
|
|
89
|
+
# Detect unnecessary COUNT queries which could be avoided
|
|
90
|
+
# with a counter_cache
|
|
91
|
+
Bullet.counter_cache_enable = false
|
|
85
92
|
```
|
|
86
93
|
|
|
87
94
|
## Whitelist
|
|
88
95
|
|
|
89
|
-
Sometimes
|
|
90
|
-
|
|
91
|
-
that you can't fix, you can add whitelist to bullet
|
|
96
|
+
Sometimes Bullet may notify you of query problems you don't care to fix, or
|
|
97
|
+
which come from outside your code. You can whitelist these to ignore them:
|
|
92
98
|
|
|
93
99
|
```ruby
|
|
94
100
|
Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
|
|
@@ -136,13 +142,13 @@ see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/u
|
|
|
136
142
|
|
|
137
143
|
## Important
|
|
138
144
|
|
|
139
|
-
If you find
|
|
145
|
+
If you find Bullet does not work for you, *please disable your browser's cache*.
|
|
140
146
|
|
|
141
147
|
## Advanced
|
|
142
148
|
|
|
143
149
|
### Profile a job
|
|
144
150
|
|
|
145
|
-
The
|
|
151
|
+
The Bullet gem uses rack middleware to profile requests. If you want to use Bullet without an http server, like to profile a job, you can use use profile method and fetch warnings
|
|
146
152
|
|
|
147
153
|
```ruby
|
|
148
154
|
Bullet.profile do
|
|
@@ -153,7 +159,7 @@ warnings = Bullet.warnings
|
|
|
153
159
|
|
|
154
160
|
### Work with sinatra
|
|
155
161
|
|
|
156
|
-
Configure and use
|
|
162
|
+
Configure and use `Bullet::Rack`
|
|
157
163
|
|
|
158
164
|
```ruby
|
|
159
165
|
configure :development do
|
|
@@ -165,7 +171,7 @@ end
|
|
|
165
171
|
|
|
166
172
|
### Run in tests
|
|
167
173
|
|
|
168
|
-
First you need to enable
|
|
174
|
+
First you need to enable Bullet in test environment.
|
|
169
175
|
|
|
170
176
|
```ruby
|
|
171
177
|
# config/environments/test.rb
|
|
@@ -176,34 +182,36 @@ config.after_initialize do
|
|
|
176
182
|
end
|
|
177
183
|
```
|
|
178
184
|
|
|
179
|
-
Then wrap each test in
|
|
185
|
+
Then wrap each test in Bullet api.
|
|
180
186
|
|
|
181
187
|
```ruby
|
|
182
188
|
# spec/spec_helper.rb
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
189
|
+
if Bullet.enable?
|
|
190
|
+
config.before(:each) do
|
|
191
|
+
Bullet.start_request
|
|
192
|
+
end
|
|
186
193
|
|
|
187
|
-
config.after(:each) do
|
|
188
|
-
|
|
189
|
-
|
|
194
|
+
config.after(:each) do
|
|
195
|
+
Bullet.perform_out_of_channel_notifications if Bullet.notification?
|
|
196
|
+
Bullet.end_request
|
|
197
|
+
end
|
|
190
198
|
end
|
|
191
199
|
```
|
|
192
200
|
|
|
193
201
|
## Debug Mode
|
|
194
202
|
|
|
195
|
-
Bullet outputs some details info, to enable debug mode, set
|
|
196
|
-
env.
|
|
203
|
+
Bullet outputs some details info, to enable debug mode, set
|
|
204
|
+
`BULLET_DEBUG=true` env.
|
|
197
205
|
|
|
198
206
|
## Contributors
|
|
199
207
|
|
|
200
208
|
[https://github.com/flyerhzm/bullet/contributors](https://github.com/flyerhzm/bullet/contributors)
|
|
201
209
|
|
|
202
|
-
##
|
|
210
|
+
## Demo
|
|
203
211
|
|
|
204
|
-
Bullet is designed to function as you browse through your application in development.
|
|
212
|
+
Bullet is designed to function as you browse through your application in development. To see it in action, follow these steps to create, detect, and fix example query problems.
|
|
205
213
|
|
|
206
|
-
1\.
|
|
214
|
+
1\. Create an example application
|
|
207
215
|
|
|
208
216
|
```
|
|
209
217
|
$ rails new test_bullet
|
|
@@ -213,7 +221,7 @@ $ rails g scaffold comment name:string post_id:integer
|
|
|
213
221
|
$ bundle exec rake db:migrate
|
|
214
222
|
```
|
|
215
223
|
|
|
216
|
-
2\.
|
|
224
|
+
2\. Change `app/model/post.rb` and `app/model/comment.rb`
|
|
217
225
|
|
|
218
226
|
```ruby
|
|
219
227
|
class Post < ActiveRecord::Base
|
|
@@ -225,7 +233,7 @@ class Comment < ActiveRecord::Base
|
|
|
225
233
|
end
|
|
226
234
|
```
|
|
227
235
|
|
|
228
|
-
3\.
|
|
236
|
+
3\. Go to `rails c` and execute
|
|
229
237
|
|
|
230
238
|
```ruby
|
|
231
239
|
post1 = Post.create(:name => 'first')
|
|
@@ -236,7 +244,7 @@ post2.comments.create(:name => 'third')
|
|
|
236
244
|
post2.comments.create(:name => 'fourth')
|
|
237
245
|
```
|
|
238
246
|
|
|
239
|
-
4\.
|
|
247
|
+
4\. Change the `app/views/posts/index.html.erb` to produce a N+1 query
|
|
240
248
|
|
|
241
249
|
```
|
|
242
250
|
<% @posts.each do |post| %>
|
|
@@ -250,7 +258,7 @@ post2.comments.create(:name => 'fourth')
|
|
|
250
258
|
<% end %>
|
|
251
259
|
```
|
|
252
260
|
|
|
253
|
-
5\.
|
|
261
|
+
5\. Add the `bullet` gem to the `Gemfile`
|
|
254
262
|
|
|
255
263
|
```ruby
|
|
256
264
|
gem "bullet"
|
|
@@ -262,7 +270,7 @@ And run
|
|
|
262
270
|
bundle install
|
|
263
271
|
```
|
|
264
272
|
|
|
265
|
-
6\. enable the
|
|
273
|
+
6\. enable the Bullet gem in development, add a line to
|
|
266
274
|
`config/environments/development.rb`
|
|
267
275
|
|
|
268
276
|
```ruby
|
|
@@ -277,13 +285,13 @@ config.after_initialize do
|
|
|
277
285
|
end
|
|
278
286
|
```
|
|
279
287
|
|
|
280
|
-
7\.
|
|
288
|
+
7\. Start the server
|
|
281
289
|
|
|
282
290
|
```
|
|
283
291
|
$ rails s
|
|
284
292
|
```
|
|
285
293
|
|
|
286
|
-
8\.
|
|
294
|
+
8\. Visit `http://localhost:3000/posts` in browser, and you will see a popup alert box that says
|
|
287
295
|
|
|
288
296
|
```
|
|
289
297
|
The request has unused preload associations as follows:
|
|
@@ -292,7 +300,7 @@ The request has N+1 queries as follows:
|
|
|
292
300
|
model: Post => associations: [comment]
|
|
293
301
|
```
|
|
294
302
|
|
|
295
|
-
which means there is a N+1 query from
|
|
303
|
+
which means there is a N+1 query from the Post object to its Comment association.
|
|
296
304
|
|
|
297
305
|
In the meanwhile, there's a log appended into `log/bullet.log` file
|
|
298
306
|
|
|
@@ -307,7 +315,7 @@ In the meanwhile, there's a log appended into `log/bullet.log` file
|
|
|
307
315
|
/home/flyerhzm/Downloads/test_bullet/app/controllers/posts_controller.rb:7:in `index'
|
|
308
316
|
```
|
|
309
317
|
|
|
310
|
-
The generated
|
|
318
|
+
The generated SQL is:
|
|
311
319
|
|
|
312
320
|
```
|
|
313
321
|
Post Load (1.0ms) SELECT * FROM "posts"
|
|
@@ -315,8 +323,7 @@ Comment Load (0.4ms) SELECT * FROM "comments" WHERE ("comments".post_id = 1)
|
|
|
315
323
|
Comment Load (0.3ms) SELECT * FROM "comments" WHERE ("comments".post_id = 2)
|
|
316
324
|
```
|
|
317
325
|
|
|
318
|
-
|
|
319
|
-
9\. fix the N+1 query, change `app/controllers/posts_controller.rb` file
|
|
326
|
+
9\. To fix the N+1 query, change `app/controllers/posts_controller.rb` file
|
|
320
327
|
|
|
321
328
|
```ruby
|
|
322
329
|
def index
|
|
@@ -329,18 +336,18 @@ def index
|
|
|
329
336
|
end
|
|
330
337
|
```
|
|
331
338
|
|
|
332
|
-
10\.
|
|
339
|
+
10\. Refresh `http://localhost:3000/posts`. Now there's no alert box and nothing new in the log.
|
|
333
340
|
|
|
334
|
-
The generated
|
|
341
|
+
The generated SQL is:
|
|
335
342
|
|
|
336
343
|
```
|
|
337
344
|
Post Load (0.5ms) SELECT * FROM "posts"
|
|
338
345
|
Comment Load (0.5ms) SELECT "comments".* FROM "comments" WHERE ("comments".post_id IN (1,2))
|
|
339
346
|
```
|
|
340
347
|
|
|
341
|
-
|
|
348
|
+
N+1 query fixed. Cool!
|
|
342
349
|
|
|
343
|
-
11\.
|
|
350
|
+
11\. Now simulate unused eager loading. Change
|
|
344
351
|
`app/controllers/posts_controller.rb` and
|
|
345
352
|
`app/views/posts/index.html.erb`
|
|
346
353
|
|
|
@@ -366,7 +373,7 @@ end
|
|
|
366
373
|
<% end %>
|
|
367
374
|
```
|
|
368
375
|
|
|
369
|
-
12\.
|
|
376
|
+
12\. Refresh `http://localhost:3000/posts`, and you will see a popup alert box that says
|
|
370
377
|
|
|
371
378
|
```
|
|
372
379
|
The request has unused preload associations as follows:
|
|
@@ -375,14 +382,14 @@ The request has N+1 queries as follows:
|
|
|
375
382
|
None
|
|
376
383
|
```
|
|
377
384
|
|
|
378
|
-
|
|
385
|
+
Meanwhile, there's a line appended to `log/bullet.log`
|
|
379
386
|
|
|
380
387
|
```
|
|
381
388
|
2009-08-25 21:13:22[INFO] Unused preload associations: PATH_INFO: /posts; model: Post => associations: [comments]·
|
|
382
389
|
Remove from your finder: :include => [:comments]
|
|
383
390
|
```
|
|
384
391
|
|
|
385
|
-
13\.
|
|
392
|
+
13\. Simulate counter_cache. Change `app/controllers/posts_controller.rb`
|
|
386
393
|
and `app/views/posts/index.html.erb`
|
|
387
394
|
|
|
388
395
|
```ruby
|
|
@@ -408,19 +415,18 @@ end
|
|
|
408
415
|
<% end %>
|
|
409
416
|
```
|
|
410
417
|
|
|
411
|
-
14\.
|
|
418
|
+
14\. Refresh `http://localhost:3000/posts`, then you will see a popup alert box that says
|
|
412
419
|
|
|
413
420
|
```
|
|
414
421
|
Need counter cache
|
|
415
422
|
Post => [:comments]
|
|
416
423
|
```
|
|
417
424
|
|
|
418
|
-
|
|
425
|
+
Meanwhile, there's a line appended to `log/bullet.log`
|
|
419
426
|
|
|
420
427
|
```
|
|
421
428
|
2009-09-11 10:07:10[INFO] Need Counter Cache
|
|
422
429
|
Post => [:comments]
|
|
423
430
|
```
|
|
424
431
|
|
|
425
|
-
|
|
426
432
|
Copyright (c) 2009 - 2014 Richard Huang (flyerhzm@gmail.com), released under the MIT license
|
data/bullet.gemspec
CHANGED
|
@@ -17,8 +17,8 @@ Gem::Specification.new do |s|
|
|
|
17
17
|
|
|
18
18
|
s.required_rubygems_version = ">= 1.3.6"
|
|
19
19
|
|
|
20
|
-
s.add_runtime_dependency "activesupport"
|
|
21
|
-
s.add_runtime_dependency "uniform_notifier", "
|
|
20
|
+
s.add_runtime_dependency "activesupport", ">= 3.0.0"
|
|
21
|
+
s.add_runtime_dependency "uniform_notifier", ">= 1.6.0"
|
|
22
22
|
|
|
23
23
|
s.files = `git ls-files`.split("\n")
|
|
24
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
@@ -119,7 +119,7 @@ module Bullet
|
|
|
119
119
|
alias_method :origin_set_inverse_instance, :set_inverse_instance
|
|
120
120
|
def set_inverse_instance(record, instance)
|
|
121
121
|
if record && we_can_set_the_inverse_on_this?(record)
|
|
122
|
-
Bullet::Detector::NPlusOneQuery.
|
|
122
|
+
Bullet::Detector::NPlusOneQuery.add_inversed_object(record, @reflection.inverse_of.name)
|
|
123
123
|
end
|
|
124
124
|
origin_set_inverse_instance(record, instance)
|
|
125
125
|
end
|
|
@@ -108,7 +108,7 @@ module Bullet
|
|
|
108
108
|
alias_method :origin_set_inverse_instance, :set_inverse_instance
|
|
109
109
|
def set_inverse_instance(record)
|
|
110
110
|
if record && invertible_for?(record)
|
|
111
|
-
Bullet::Detector::NPlusOneQuery.
|
|
111
|
+
Bullet::Detector::NPlusOneQuery.add_inversed_object(record, inverse_reflection_for(record).name)
|
|
112
112
|
end
|
|
113
113
|
origin_set_inverse_instance(record)
|
|
114
114
|
end
|
|
@@ -98,22 +98,12 @@ module Bullet
|
|
|
98
98
|
alias_method :origin_reader, :reader
|
|
99
99
|
def reader(force_reload = false)
|
|
100
100
|
result = origin_reader(force_reload)
|
|
101
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
101
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
102
102
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
103
103
|
result
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
::ActiveRecord::Associations::Association.class_eval do
|
|
108
|
-
alias_method :origin_set_inverse_instance, :set_inverse_instance
|
|
109
|
-
def set_inverse_instance(record)
|
|
110
|
-
if record && invertible_for?(record)
|
|
111
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(record)
|
|
112
|
-
end
|
|
113
|
-
origin_set_inverse_instance(record)
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
107
|
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
118
108
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
119
109
|
|
|
@@ -27,6 +27,7 @@ module Bullet
|
|
|
27
27
|
def preloaders_on(association, records, scope)
|
|
28
28
|
if records.first.class.name !~ /^HABTM_/
|
|
29
29
|
records.each do |record|
|
|
30
|
+
next unless record
|
|
30
31
|
Bullet::Detector::Association.add_object_associations(record, association)
|
|
31
32
|
end
|
|
32
33
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
@@ -5,19 +5,19 @@ module Bullet
|
|
|
5
5
|
def add_object_associations(object, associations)
|
|
6
6
|
return unless Bullet.start?
|
|
7
7
|
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
|
8
|
-
return unless object.
|
|
8
|
+
return unless object.primary_key_value
|
|
9
9
|
|
|
10
|
-
Bullet.debug("Detector::Association#add_object_associations", "object: #{object.
|
|
11
|
-
object_associations.add(object.
|
|
10
|
+
Bullet.debug("Detector::Association#add_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
|
|
11
|
+
object_associations.add(object.bullet_key, associations)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def add_call_object_associations(object, associations)
|
|
15
15
|
return unless Bullet.start?
|
|
16
16
|
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
|
17
|
-
return unless object.
|
|
17
|
+
return unless object.primary_key_value
|
|
18
18
|
|
|
19
|
-
Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.
|
|
20
|
-
call_object_associations.add(object.
|
|
19
|
+
Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
|
|
20
|
+
call_object_associations.add(object.bullet_key, associations)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private
|
|
@@ -48,13 +48,19 @@ module Bullet
|
|
|
48
48
|
# impossible_objects keep the class to objects relationships
|
|
49
49
|
# that the objects may not cause N+1 query.
|
|
50
50
|
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
51
|
-
# Notice: impossible_objects are not accurate,
|
|
52
51
|
# if find collection returns only one object, then the object is impossible object,
|
|
53
52
|
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
|
54
53
|
def impossible_objects
|
|
55
54
|
Thread.current[:bullet_impossible_objects]
|
|
56
55
|
end
|
|
57
56
|
|
|
57
|
+
# inversed_objects keeps object relationships
|
|
58
|
+
# that association is inversed.
|
|
59
|
+
# e.g. { "Comment:1" => ["post"] }
|
|
60
|
+
def inversed_objects
|
|
61
|
+
Thread.current[:bullet_inversed_objects]
|
|
62
|
+
end
|
|
63
|
+
|
|
58
64
|
# eager_loadings keep the object relationships
|
|
59
65
|
# that the associations are preloaded by find :include.
|
|
60
66
|
# e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
|
|
@@ -5,10 +5,10 @@ module Bullet
|
|
|
5
5
|
def add_counter_cache(object, associations)
|
|
6
6
|
return unless Bullet.start?
|
|
7
7
|
return unless Bullet.counter_cache_enable?
|
|
8
|
-
return unless object.
|
|
8
|
+
return unless object.primary_key_value
|
|
9
9
|
|
|
10
|
-
Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.
|
|
11
|
-
if conditions_met?(object.
|
|
10
|
+
Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
|
|
11
|
+
if conditions_met?(object.bullet_key, associations)
|
|
12
12
|
create_notification object.class.to_s, associations
|
|
13
13
|
end
|
|
14
14
|
end
|
|
@@ -17,19 +17,19 @@ module Bullet
|
|
|
17
17
|
return unless Bullet.start?
|
|
18
18
|
return unless Bullet.counter_cache_enable?
|
|
19
19
|
objects = Array(object_or_objects)
|
|
20
|
-
return if objects.map(&:
|
|
20
|
+
return if objects.map(&:primary_key_value).compact.empty?
|
|
21
21
|
|
|
22
|
-
Bullet.debug("Detector::CounterCache#add_possible_objects", "objects: #{objects.map(&:
|
|
23
|
-
objects.each { |object| possible_objects.add object.
|
|
22
|
+
Bullet.debug("Detector::CounterCache#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
|
|
23
|
+
objects.each { |object| possible_objects.add object.bullet_key }
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def add_impossible_object(object)
|
|
27
27
|
return unless Bullet.start?
|
|
28
28
|
return unless Bullet.counter_cache_enable?
|
|
29
|
-
return unless object.
|
|
29
|
+
return unless object.primary_key_value
|
|
30
30
|
|
|
31
|
-
Bullet.debug("Detector::CounterCache#add_impossible_object", "object: #{object.
|
|
32
|
-
impossible_objects.add object.
|
|
31
|
+
Bullet.debug("Detector::CounterCache#add_impossible_object", "object: #{object.bullet_key}")
|
|
32
|
+
impossible_objects.add object.bullet_key
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
private
|
|
@@ -50,8 +50,8 @@ module Bullet
|
|
|
50
50
|
Thread.current[:bullet_counter_impossible_objects]
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
def conditions_met?(
|
|
54
|
-
possible_objects.include?(
|
|
53
|
+
def conditions_met?(bullet_key, associations)
|
|
54
|
+
possible_objects.include?(bullet_key) && !impossible_objects.include?(bullet_key)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
end
|
|
@@ -10,12 +10,13 @@ module Bullet
|
|
|
10
10
|
# if it is, keeps this unpreload associations and caller.
|
|
11
11
|
def call_association(object, associations)
|
|
12
12
|
return unless Bullet.start?
|
|
13
|
-
return unless object.
|
|
13
|
+
return unless object.primary_key_value
|
|
14
|
+
return if inversed_objects.include?(object.bullet_key, associations)
|
|
14
15
|
add_call_object_associations(object, associations)
|
|
15
16
|
|
|
16
|
-
Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.
|
|
17
|
-
if conditions_met?(object.
|
|
18
|
-
Bullet.debug("detect n + 1 query", "object: #{object.
|
|
17
|
+
Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_key}, associations: #{associations}")
|
|
18
|
+
if conditions_met?(object.bullet_key, associations)
|
|
19
|
+
Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
|
|
19
20
|
create_notification caller_in_project, object.class.to_s, associations
|
|
20
21
|
end
|
|
21
22
|
end
|
|
@@ -24,19 +25,28 @@ module Bullet
|
|
|
24
25
|
return unless Bullet.start?
|
|
25
26
|
return unless Bullet.n_plus_one_query_enable?
|
|
26
27
|
objects = Array(object_or_objects)
|
|
27
|
-
return if objects.map(&:
|
|
28
|
+
return if objects.map(&:primary_key_value).compact.empty?
|
|
28
29
|
|
|
29
|
-
Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:
|
|
30
|
-
objects.each { |object| possible_objects.add object.
|
|
30
|
+
Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
|
|
31
|
+
objects.each { |object| possible_objects.add object.bullet_key }
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def add_impossible_object(object)
|
|
34
35
|
return unless Bullet.start?
|
|
35
36
|
return unless Bullet.n_plus_one_query_enable?
|
|
36
|
-
return unless object.
|
|
37
|
+
return unless object.primary_key_value
|
|
37
38
|
|
|
38
|
-
Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.
|
|
39
|
-
impossible_objects.add object.
|
|
39
|
+
Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.bullet_key}")
|
|
40
|
+
impossible_objects.add object.bullet_key
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def add_inversed_object(object, association)
|
|
44
|
+
return unless Bullet.start?
|
|
45
|
+
return unless Bullet.n_plus_one_query_enable?
|
|
46
|
+
return unless object.primary_key_value
|
|
47
|
+
|
|
48
|
+
Bullet.debug("Detector::NPlusOneQuery#add_inversed_object", "object: #{object.bullet_key}, association: #{association}")
|
|
49
|
+
inversed_objects.add object.bullet_key, association
|
|
40
50
|
end
|
|
41
51
|
|
|
42
52
|
private
|
|
@@ -50,8 +60,8 @@ module Bullet
|
|
|
50
60
|
end
|
|
51
61
|
|
|
52
62
|
# decide whether the object.associations is unpreloaded or not.
|
|
53
|
-
def conditions_met?(
|
|
54
|
-
possible?(
|
|
63
|
+
def conditions_met?(bullet_key, associations)
|
|
64
|
+
possible?(bullet_key) && !impossible?(bullet_key) && !association?(bullet_key, associations)
|
|
55
65
|
end
|
|
56
66
|
|
|
57
67
|
def caller_in_project
|
|
@@ -63,17 +73,17 @@ module Bullet
|
|
|
63
73
|
end
|
|
64
74
|
end
|
|
65
75
|
|
|
66
|
-
def possible?(
|
|
67
|
-
possible_objects.include?
|
|
76
|
+
def possible?(bullet_key)
|
|
77
|
+
possible_objects.include? bullet_key
|
|
68
78
|
end
|
|
69
79
|
|
|
70
|
-
def impossible?(
|
|
71
|
-
impossible_objects.include?
|
|
80
|
+
def impossible?(bullet_key)
|
|
81
|
+
impossible_objects.include? bullet_key
|
|
72
82
|
end
|
|
73
83
|
|
|
74
84
|
# check if object => associations already exists in object_associations.
|
|
75
|
-
def association?(
|
|
76
|
-
value = object_associations[
|
|
85
|
+
def association?(bullet_key, associations)
|
|
86
|
+
value = object_associations[bullet_key]
|
|
77
87
|
if value
|
|
78
88
|
value.each do |v|
|
|
79
89
|
result = v.is_a?(Hash) ? v.has_key?(associations) : v == associations
|