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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb27c10f2796dc6cc38899be2b88943f6c124d80
4
- data.tar.gz: c89d95e08212b3bf8da49f2ce4c542ec2aa3021d
3
+ metadata.gz: b32f7ec78bd32da2e77680a04a02aefb3cd6615f
4
+ data.tar.gz: d946cc0ded11f343b047bb16145cc72c703d15c9
5
5
  SHA512:
6
- metadata.gz: 8f4d67bebf52a6730fa623bb150717e56c052605fc339f37d89d915af0981e442200e2bfee5c3405e6269d0907dc4e4f89fd2965000e637555aa8614580837c0
7
- data.tar.gz: 3fbfc65e37e50444107e153050b33f66fe51037fd9d325438015d51c71f4a5ccb387e9b76ba28a2c753c9f9227653686535c857f457c80d62213cdda070b9c18
6
+ metadata.gz: 9c27d586ccdee749e304f57df64c7c17cc77ea88689d5e60b4ae53d19cf25447fe9758fc8c968ca0016a1bb8c5068af1cefd6a76bf07725bcb7c0ffc099ca345
7
+ data.tar.gz: a864ff44451a06069e812bab6b41826cef78ceed6e25949fd630a0399271d7fb3b25e2b43b619ea74956f7ec58c5886045e5766f7ec9efb8e5f2f2e0c197acac
@@ -1,21 +1,27 @@
1
1
  ## Next Release
2
2
 
3
- ## 5.7.5 (12/03/2018)
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/03/2018)
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/02/2018)
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/01/2018)
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 = true
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 use profile method and fetch warnings
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
@@ -1,4 +1,4 @@
1
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
1
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
2
2
  require 'bundler'
3
3
  Bundler.setup
4
4
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib/', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'bullet/version'
@@ -48,21 +48,12 @@ module Bullet
48
48
  end
49
49
 
50
50
  ::ActiveRecord::Persistence.class_eval do
51
- def save_with_bullet(*args, &proc)
52
- was_new_record = new_record?
53
- save_without_bullet(*args, &proc).tap do |result|
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 :save, :bullet
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 save_with_bullet(*args, &proc)
54
- was_new_record = new_record?
55
- save_without_bullet(*args, &proc).tap do |result|
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 :save, :bullet
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 save_with_bullet(*args, &proc)
48
- was_new_record = new_record?
49
- save_without_bullet(*args, &proc).tap do |result|
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 :save, :bullet
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 save(*args)
6
- was_new_record = new_record?
7
- super(*args).tap do |result|
8
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
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 save(*args)
6
- was_new_record = new_record?
7
- super(*args).tap do |result|
8
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
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
@@ -29,7 +29,7 @@ module Bullet
29
29
  elsif active_record52?
30
30
  'active_record52'
31
31
  else
32
- raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
32
+ raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
33
33
  end
34
34
  end
35
35
  end
@@ -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 |caller_path|
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? do |include_pattern|
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? do |location|
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 Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
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
- caller_path = location.absolute_path.to_s
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '5.7.5'.freeze
4
+ VERSION = '5.7.6'.freeze
5
5
  end
@@ -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
@@ -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.5
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-03-12 00:00:00.000000000 Z
11
+ date: 2018-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport