bullet 5.7.5 → 5.7.6
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 -4
- data/README.md +6 -3
- data/Rakefile +1 -1
- data/bullet.gemspec +1 -1
- data/lib/bullet/active_record4.rb +12 -13
- data/lib/bullet/active_record41.rb +12 -13
- data/lib/bullet/active_record42.rb +12 -13
- data/lib/bullet/active_record5.rb +11 -11
- data/lib/bullet/active_record52.rb +11 -11
- data/lib/bullet/dependency.rb +1 -1
- data/lib/bullet/stack_trace_filter.rb +40 -21
- data/lib/bullet/version.rb +1 -1
- data/spec/bullet/detector/n_plus_one_query_spec.rb +23 -0
- data/spec/integration/active_record/association_spec.rb +22 -0
- data/spec/models/post.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b32f7ec78bd32da2e77680a04a02aefb3cd6615f
|
4
|
+
data.tar.gz: d946cc0ded11f343b047bb16145cc72c703d15c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c27d586ccdee749e304f57df64c7c17cc77ea88689d5e60b4ae53d19cf25447fe9758fc8c968ca0016a1bb8c5068af1cefd6a76bf07725bcb7c0ffc099ca345
|
7
|
+
data.tar.gz: a864ff44451a06069e812bab6b41826cef78ceed6e25949fd630a0399271d7fb3b25e2b43b619ea74956f7ec58c5886045e5766f7ec9efb8e5f2f2e0c197acac
|
data/CHANGELOG.md
CHANGED
@@ -1,21 +1,27 @@
|
|
1
1
|
## Next Release
|
2
2
|
|
3
|
-
## 5.7.
|
3
|
+
## 5.7.6 (08/15/2018)
|
4
|
+
|
5
|
+
* Extend stacktrace matching for sub-file precision
|
6
|
+
* Fix false positive in after_save/_create callbacks
|
7
|
+
* Don't triger a preload error on "manual" preloads
|
8
|
+
|
9
|
+
## 5.7.5 (03/12/2018)
|
4
10
|
|
5
11
|
* Fix duplicate logs in mongoid 4.x and 5.x version
|
6
12
|
* Add magic comment frozen_string_literal: true
|
7
13
|
|
8
|
-
## 5.7.4 (10/
|
14
|
+
## 5.7.4 (03/10/2018)
|
9
15
|
|
10
16
|
* Avoid Bullet from making extra queries in mongoid6
|
11
17
|
* Use caller for ruby 1.9 while caller_locations for 2.0+
|
12
18
|
|
13
|
-
## 5.7.3 (17/
|
19
|
+
## 5.7.3 (02/17/2018)
|
14
20
|
|
15
21
|
* Exclude configured bundler path in addition to '/vendor'
|
16
22
|
* Support rails 5.1.5
|
17
23
|
|
18
|
-
## 5.7.2 (18/
|
24
|
+
## 5.7.2 (01/18/2018)
|
19
25
|
|
20
26
|
* Fix `caller_path` in `excluded_stacktrace_path`
|
21
27
|
|
data/README.md
CHANGED
@@ -64,7 +64,7 @@ config.after_initialize do
|
|
64
64
|
Bullet.rollbar = true
|
65
65
|
Bullet.add_footer = true
|
66
66
|
Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
|
67
|
-
Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware' ]
|
67
|
+
Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware', ['my_file.rb', 'my_method'], ['my_file.rb', 16..20] ]
|
68
68
|
Bullet.slack = { webhook_url: 'http://some.slack.url', channel: '#default', username: 'notifier' }
|
69
69
|
end
|
70
70
|
```
|
@@ -87,6 +87,8 @@ The code above will enable all of the Bullet notification systems:
|
|
87
87
|
* `Bullet.add_footer`: adds the details in the bottom left corner of the page. Double click the footer or use close button to hide footer.
|
88
88
|
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
|
89
89
|
* `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
|
90
|
+
Each item can be a string (match substring), a regex, or an array where the first item is a path to match, and the second
|
91
|
+
item is a line number, a Range of line numbers, or a (bare) method name, to exclude only particular lines in a file.
|
90
92
|
* `Bullet.slack`: add notifications to slack
|
91
93
|
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
92
94
|
|
@@ -126,10 +128,11 @@ class ApplicationController < ActionController::Base
|
|
126
128
|
around_action :skip_bullet
|
127
129
|
|
128
130
|
def skip_bullet
|
131
|
+
previous_value = Bullet.enabled?
|
129
132
|
Bullet.enable = false
|
130
133
|
yield
|
131
134
|
ensure
|
132
|
-
Bullet.enable =
|
135
|
+
Bullet.enable = previous_value
|
133
136
|
end
|
134
137
|
end
|
135
138
|
```
|
@@ -180,7 +183,7 @@ If you find Bullet does not work for you, *please disable your browser's cache*.
|
|
180
183
|
|
181
184
|
### Profile a job
|
182
185
|
|
183
|
-
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
|
186
|
+
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 the profile method and fetch warnings
|
184
187
|
|
185
188
|
```ruby
|
186
189
|
Bullet.profile do
|
data/Rakefile
CHANGED
data/bullet.gemspec
CHANGED
@@ -48,21 +48,12 @@ module Bullet
|
|
48
48
|
end
|
49
49
|
|
50
50
|
::ActiveRecord::Persistence.class_eval do
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
51
|
+
def _create_record_with_bullet(*args)
|
52
|
+
_create_record_without_bullet(*args).tap do
|
53
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
55
54
|
end
|
56
55
|
end
|
57
|
-
alias_method_chain :
|
58
|
-
|
59
|
-
def save_with_bullet!(*args, &proc)
|
60
|
-
was_new_record = new_record?
|
61
|
-
save_without_bullet!(*args, &proc).tap do |result|
|
62
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
63
|
-
end
|
64
|
-
end
|
65
|
-
alias_method_chain :save!, :bullet
|
56
|
+
alias_method_chain :_create_record, :bullet
|
66
57
|
end
|
67
58
|
|
68
59
|
::ActiveRecord::Associations::Preloader.class_eval do
|
@@ -199,6 +190,14 @@ module Bullet
|
|
199
190
|
end
|
200
191
|
# rubocop:enable Style/MethodCallWithoutArgsParentheses
|
201
192
|
end
|
193
|
+
|
194
|
+
::ActiveRecord::Associations::BelongsToAssociation.class_eval do
|
195
|
+
def writer_with_bullet(record)
|
196
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
197
|
+
writer_without_bullet(record)
|
198
|
+
end
|
199
|
+
alias_method_chain :writer, :bullet
|
200
|
+
end
|
202
201
|
end
|
203
202
|
end
|
204
203
|
end
|
@@ -50,21 +50,12 @@ module Bullet
|
|
50
50
|
end
|
51
51
|
|
52
52
|
::ActiveRecord::Persistence.class_eval do
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
53
|
+
def _create_record_with_bullet(*args)
|
54
|
+
_create_record_without_bullet(*args).tap do
|
55
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
57
56
|
end
|
58
57
|
end
|
59
|
-
alias_method_chain :
|
60
|
-
|
61
|
-
def save_with_bullet!(*args, &proc)
|
62
|
-
was_new_record = new_record?
|
63
|
-
save_without_bullet!(*args, &proc).tap do |result|
|
64
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
65
|
-
end
|
66
|
-
end
|
67
|
-
alias_method_chain :save!, :bullet
|
58
|
+
alias_method_chain :_create_record, :bullet
|
68
59
|
end
|
69
60
|
|
70
61
|
::ActiveRecord::Associations::Preloader.class_eval do
|
@@ -187,6 +178,14 @@ module Bullet
|
|
187
178
|
origin_count_records
|
188
179
|
end
|
189
180
|
end
|
181
|
+
|
182
|
+
::ActiveRecord::Associations::BelongsToAssociation.class_eval do
|
183
|
+
def writer_with_bullet(record)
|
184
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
185
|
+
writer_without_bullet(record)
|
186
|
+
end
|
187
|
+
alias_method_chain :writer, :bullet
|
188
|
+
end
|
190
189
|
end
|
191
190
|
end
|
192
191
|
end
|
@@ -44,21 +44,12 @@ module Bullet
|
|
44
44
|
end
|
45
45
|
|
46
46
|
::ActiveRecord::Persistence.class_eval do
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
47
|
+
def _create_record_with_bullet(*args)
|
48
|
+
_create_record_without_bullet(*args).tap do
|
49
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
51
50
|
end
|
52
51
|
end
|
53
|
-
alias_method_chain :
|
54
|
-
|
55
|
-
def save_with_bullet!(*args, &proc)
|
56
|
-
was_new_record = new_record?
|
57
|
-
save_without_bullet!(*args, &proc).tap do |result|
|
58
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
59
|
-
end
|
60
|
-
end
|
61
|
-
alias_method_chain :save!, :bullet
|
52
|
+
alias_method_chain :_create_record, :bullet
|
62
53
|
end
|
63
54
|
|
64
55
|
::ActiveRecord::Relation.class_eval do
|
@@ -247,6 +238,14 @@ module Bullet
|
|
247
238
|
origin_count_records
|
248
239
|
end
|
249
240
|
end
|
241
|
+
|
242
|
+
::ActiveRecord::Associations::BelongsToAssociation.class_eval do
|
243
|
+
def writer_with_bullet(record)
|
244
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
245
|
+
writer_without_bullet(record)
|
246
|
+
end
|
247
|
+
alias_method_chain :writer, :bullet
|
248
|
+
end
|
250
249
|
end
|
251
250
|
end
|
252
251
|
end
|
@@ -2,17 +2,10 @@
|
|
2
2
|
|
3
3
|
module Bullet
|
4
4
|
module SaveWithBulletSupport
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def save!(*args)
|
13
|
-
was_new_record = new_record?
|
14
|
-
super(*args).tap do |result|
|
15
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
5
|
+
def _create_record(*)
|
6
|
+
super do
|
7
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
8
|
+
yield(self) if block_given?
|
16
9
|
end
|
17
10
|
end
|
18
11
|
end
|
@@ -238,6 +231,13 @@ module Bullet
|
|
238
231
|
super
|
239
232
|
end
|
240
233
|
end)
|
234
|
+
|
235
|
+
::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
|
236
|
+
def writer(record)
|
237
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
238
|
+
super
|
239
|
+
end
|
240
|
+
end)
|
241
241
|
end
|
242
242
|
end
|
243
243
|
end
|
@@ -2,17 +2,10 @@
|
|
2
2
|
|
3
3
|
module Bullet
|
4
4
|
module SaveWithBulletSupport
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def save!(*args)
|
13
|
-
was_new_record = new_record?
|
14
|
-
super(*args).tap do |result|
|
15
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
5
|
+
def _create_record(*)
|
6
|
+
super do
|
7
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
8
|
+
yield(self) if block_given?
|
16
9
|
end
|
17
10
|
end
|
18
11
|
end
|
@@ -221,6 +214,13 @@ module Bullet
|
|
221
214
|
super
|
222
215
|
end
|
223
216
|
end)
|
217
|
+
|
218
|
+
::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
|
219
|
+
def writer(record)
|
220
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
221
|
+
super
|
222
|
+
end
|
223
|
+
end)
|
224
224
|
end
|
225
225
|
end
|
226
226
|
end
|
data/lib/bullet/dependency.rb
CHANGED
@@ -8,46 +8,65 @@ module Bullet
|
|
8
8
|
app_root = rails? ? Rails.root.to_s : Dir.pwd
|
9
9
|
vendor_root = app_root + VENDOR_PATH
|
10
10
|
bundler_path = Bundler.bundle_path.to_s
|
11
|
-
select_caller_locations do |
|
11
|
+
select_caller_locations do |location|
|
12
|
+
caller_path = location_as_path(location)
|
12
13
|
caller_path.include?(app_root) && !caller_path.include?(vendor_root) && !caller_path.include?(bundler_path) ||
|
13
|
-
Bullet.stacktrace_includes.any?
|
14
|
-
case include_pattern
|
15
|
-
when String
|
16
|
-
caller_path.include?(include_pattern)
|
17
|
-
when Regexp
|
18
|
-
caller_path =~ include_pattern
|
19
|
-
end
|
20
|
-
end
|
14
|
+
Bullet.stacktrace_includes.any? { |include_pattern| pattern_matches?(location, include_pattern) }
|
21
15
|
end
|
22
16
|
end
|
23
17
|
|
24
18
|
def excluded_stacktrace_path?
|
25
19
|
Bullet.stacktrace_excludes.any? do |exclude_pattern|
|
26
|
-
caller_in_project.any?
|
27
|
-
caller_path = location.absolute_path.to_s
|
28
|
-
case exclude_pattern
|
29
|
-
when String
|
30
|
-
caller_path.include?(exclude_pattern)
|
31
|
-
when Regexp
|
32
|
-
caller_path =~ exclude_pattern
|
33
|
-
end
|
34
|
-
end
|
20
|
+
caller_in_project.any? { |location| pattern_matches?(location, exclude_pattern) }
|
35
21
|
end
|
36
22
|
end
|
37
23
|
|
38
24
|
private
|
39
25
|
|
26
|
+
def pattern_matches?(location, pattern)
|
27
|
+
path = location_as_path(location)
|
28
|
+
case pattern
|
29
|
+
when Array
|
30
|
+
pattern_path = pattern.first
|
31
|
+
filter = pattern.last
|
32
|
+
return false unless pattern_matches?(location, pattern_path)
|
33
|
+
|
34
|
+
case filter
|
35
|
+
when Range
|
36
|
+
filter.include?(location.lineno)
|
37
|
+
when Integer
|
38
|
+
filter == location.lineno
|
39
|
+
when String
|
40
|
+
filter == location.base_label
|
41
|
+
end
|
42
|
+
when String
|
43
|
+
path.include?(pattern)
|
44
|
+
when Regexp
|
45
|
+
path =~ pattern
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def location_as_path(location)
|
50
|
+
ruby_19? ? location : location.absolute_path.to_s
|
51
|
+
end
|
52
|
+
|
40
53
|
def select_caller_locations
|
41
|
-
if
|
54
|
+
if ruby_19?
|
42
55
|
caller.select do |caller_path|
|
43
56
|
yield caller_path
|
44
57
|
end
|
45
58
|
else
|
46
59
|
caller_locations.select do |location|
|
47
|
-
|
48
|
-
yield caller_path
|
60
|
+
yield location
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
64
|
+
|
65
|
+
def ruby_19?
|
66
|
+
if @ruby_19.nil?
|
67
|
+
@ruby_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
68
|
+
end
|
69
|
+
@ruby_19
|
70
|
+
end
|
52
71
|
end
|
53
72
|
end
|
data/lib/bullet/version.rb
CHANGED
@@ -101,6 +101,29 @@ module Bullet
|
|
101
101
|
expect(NPlusOneQuery).to_not receive(:create_notification)
|
102
102
|
NPlusOneQuery.call_association(@post, :association)
|
103
103
|
end
|
104
|
+
|
105
|
+
# just a sanity spec to make sure the following spec works correctly
|
106
|
+
it "should create notification when stacktrace contains methods that aren't in the exclude list" do
|
107
|
+
method = NPlusOneQuery.method(:excluded_stacktrace_path?).source_location
|
108
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
109
|
+
excluded_path = OpenStruct.new(absolute_path: method.first, lineno: method.last)
|
110
|
+
|
111
|
+
expect(NPlusOneQuery).to receive(:caller_locations).at_least(1).and_return([in_project, excluded_path])
|
112
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).and_return(true)
|
113
|
+
expect(NPlusOneQuery).to receive(:create_notification)
|
114
|
+
NPlusOneQuery.call_association(@post, :association)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should not create notification when stacktrace contains methods that are in the exclude list' do
|
118
|
+
method = NPlusOneQuery.method(:excluded_stacktrace_path?).source_location
|
119
|
+
Bullet.stacktrace_excludes = [method]
|
120
|
+
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
121
|
+
excluded_path = OpenStruct.new(absolute_path: method.first, lineno: method.last)
|
122
|
+
|
123
|
+
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, excluded_path])
|
124
|
+
expect(NPlusOneQuery).to_not receive(:create_notification)
|
125
|
+
NPlusOneQuery.call_association(@post, :association)
|
126
|
+
end
|
104
127
|
end
|
105
128
|
end
|
106
129
|
|
@@ -356,6 +356,28 @@ if active_record?
|
|
356
356
|
|
357
357
|
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
358
358
|
end
|
359
|
+
|
360
|
+
it 'should not detect newly assigned object in an after_save' do
|
361
|
+
new_post = Post.new(category: Category.first)
|
362
|
+
|
363
|
+
new_post.trigger_after_save = true
|
364
|
+
new_post.save!
|
365
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
366
|
+
|
367
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should not detect "manual" preload' do
|
371
|
+
comment = Comment.all.to_a.first
|
372
|
+
post = Post.find(comment.post_id)
|
373
|
+
# "manually" preload with out-of-band data
|
374
|
+
comment.post = post
|
375
|
+
# loading it should not trigger anything
|
376
|
+
comment.post
|
377
|
+
|
378
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
379
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
380
|
+
end
|
359
381
|
end
|
360
382
|
|
361
383
|
context 'comment => post => category' do
|
data/spec/models/post.rb
CHANGED
@@ -14,4 +14,19 @@ class Post < ActiveRecord::Base
|
|
14
14
|
def link=(*)
|
15
15
|
comments.new
|
16
16
|
end
|
17
|
+
|
18
|
+
# see association_spec.rb 'should not detect newly assigned object in an after_save'
|
19
|
+
attr_accessor :trigger_after_save
|
20
|
+
after_save do
|
21
|
+
next unless trigger_after_save
|
22
|
+
|
23
|
+
temp_comment = Comment.new(post: self)
|
24
|
+
# this triggers self to be "possible", even though it's
|
25
|
+
# not saved yet
|
26
|
+
temp_comment.post
|
27
|
+
|
28
|
+
# category should NOT whine about not being pre-loaded, because
|
29
|
+
# it's obviously attached to a new object
|
30
|
+
category
|
31
|
+
end
|
17
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.7.
|
4
|
+
version: 5.7.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|