bullet 5.7.5 → 5.7.6

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