bullet 8.0.0 → 8.0.2

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
  SHA256:
3
- metadata.gz: '0309ea5abf7ab699c893c093abed5605b8afbb93ca1034cac1af01d8afcbd0da'
4
- data.tar.gz: fe9a9a6b61d3c2d9235fb9bfe9f7ba4f141fca5dccf10707ffee86dfcd84b9c3
3
+ metadata.gz: 69ace15062b915463985d5257ca7b054b482ca842f05f832dc45c1134bc7c8cd
4
+ data.tar.gz: a43533c8d894791edffcb4cbd9ad4ceba1dd238a8b736d23e3444ca7f766c57c
5
5
  SHA512:
6
- metadata.gz: dbc0c561b35efc8a3e131aae16f75382c319f2a6cffeeb48bee8cfa738d88eacd2b53026c59cdcf923e691bc785f9413fa0754cccb2419f2589c36193a25b7da
7
- data.tar.gz: f9a723c0531c67c673612da26db9d1a3a365f9d912fd8d9dbdf2e4bcd24ec89f18f3662c743e5a201d89c6e886aacea92880b0f3460613b9a8f10be0dff2ef7b
6
+ metadata.gz: 262e12b9be4eeae23494a06eb71d6c6823674ecfa8ec9a0c5a5eaff7d7bf268a52cb35ee8198cf0712312745735bc4ad2a6474e3a6a5a6fa8820ed896754795a
7
+ data.tar.gz: b16503b483682cb94121442f8ee1d74145cb835031e62a864765629b0dbffd6b360136ee3b09f1b14d1e35a25794c06ace1579415829ad0dfdcff1ae2be0b3bf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## Next Release
2
2
 
3
+ ## 8.0.2 (04/02/2025)
4
+
5
+ * Do not cache `bullet_key` if object is not persisted
6
+
7
+ ## 8.0.1 (02/10/2025)
8
+
9
+ * Update benchmark to use sqlite
10
+ * Reduce mem allocations
11
+ * Require active_support's inflections module before requiring the delegation module
12
+
3
13
  ## 8.0.0 (11/10/2024)
4
14
 
5
15
  * Support Rails 8
data/README.md CHANGED
@@ -121,8 +121,6 @@ Bullet.unused_eager_loading_enable = false
121
121
  Bullet.counter_cache_enable = false
122
122
  ```
123
123
 
124
- Note: When calling `Bullet.enable`, all other detectors are reset to their defaults (`true`) and need reconfiguring.
125
-
126
124
  ## Safe list
127
125
 
128
126
  Sometimes Bullet may notify you of query problems you don't care to fix, or
@@ -240,7 +238,7 @@ If your application generates a Content-Security-Policy via a separate middlewar
240
238
 
241
239
  ### Run in tests
242
240
 
243
- First you need to enable Bullet in test environment.
241
+ First you need to enable Bullet in the test environment.
244
242
 
245
243
  ```ruby
246
244
  # config/environments/test.rb
@@ -251,11 +249,13 @@ config.after_initialize do
251
249
  end
252
250
  ```
253
251
 
254
- Then wrap each test in Bullet api.
252
+ Then wrap each test in the Bullet api.
253
+
254
+ With RSpec:
255
255
 
256
256
  ```ruby
257
257
  # spec/rails_helper.rb
258
- if Bullet.enable?
258
+ RSpec.configure do |config|
259
259
  config.before(:each) do
260
260
  Bullet.start_request
261
261
  end
@@ -267,6 +267,26 @@ if Bullet.enable?
267
267
  end
268
268
  ```
269
269
 
270
+ With Minitest:
271
+
272
+ ```ruby
273
+ # test/test_helper.rb
274
+ module ActiveSupport
275
+ class TestCase
276
+ def before_setup
277
+ Bullet.start_request
278
+ super
279
+ end
280
+
281
+ def after_teardown
282
+ super
283
+ Bullet.perform_out_of_channel_notifications if Bullet.notification?
284
+ Bullet.end_request
285
+ end
286
+ end
287
+ end
288
+ ```
289
+
270
290
  ## Debug Mode
271
291
 
272
292
  Bullet outputs some details info, to enable debug mode, set
@@ -56,6 +56,8 @@ module Bullet
56
56
  'mongoid7x'
57
57
  elsif mongoid8x?
58
58
  'mongoid8x'
59
+ elsif mongoid9x?
60
+ 'mongoid9x'
59
61
  else
60
62
  raise "Bullet does not support mongoid #{::Mongoid::VERSION} yet"
61
63
  end
@@ -149,5 +151,9 @@ module Bullet
149
151
  def mongoid8x?
150
152
  mongoid? && ::Mongoid::VERSION =~ /\A8/
151
153
  end
154
+
155
+ def mongoid9x?
156
+ mongoid? && ::Mongoid::VERSION =~ /\A9/
157
+ end
152
158
  end
153
159
  end
@@ -81,7 +81,7 @@ module Bullet
81
81
  Thread.current.thread_variable_get(:bullet_eager_loadings)
82
82
  end
83
83
 
84
- # cal_stacks keeps stacktraces where querie-objects were called from.
84
+ # call_stacks keeps stacktraces where querie-objects were called from.
85
85
  # e.g. { 'Object:111' => [SomeProject/app/controllers/...] }
86
86
  def call_stacks
87
87
  Thread.current.thread_variable_get(:bullet_call_stacks)
@@ -27,7 +27,7 @@ module Bullet
27
27
  )
28
28
  if !excluded_stacktrace_path? && conditions_met?(object, associations)
29
29
  Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
30
- create_notification caller_in_project(object.bullet_key), object.class.to_s, associations
30
+ create_notification(caller_in_project(object.bullet_key), object.class.to_s, associations)
31
31
  end
32
32
  end
33
33
 
@@ -38,16 +38,17 @@ module Bullet
38
38
  objects = Array.wrap(object_or_objects)
39
39
  class_names_match_regex = true
40
40
  primary_key_values_are_empty = true
41
- keys_joined = ""
42
- objects.each do |obj|
41
+
42
+ keys_joined = objects.map do |obj|
43
43
  unless obj.class.name =~ /^HABTM_/
44
44
  class_names_match_regex = false
45
45
  end
46
46
  unless obj.bullet_primary_key_value.nil?
47
47
  primary_key_values_are_empty = false
48
48
  end
49
- keys_joined += "#{(keys_joined.empty? ? '' : ', ')}#{obj.bullet_key}"
50
- end
49
+ obj.bullet_key
50
+ end.join(", ")
51
+
51
52
  unless class_names_match_regex || primary_key_values_are_empty
52
53
  Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{keys_joined}")
53
54
  objects.each { |object| possible_objects.add object.bullet_key }
@@ -4,22 +4,23 @@ module Bullet
4
4
  module Ext
5
5
  module Object
6
6
  refine ::Object do
7
+ attr_writer :bullet_key, :bullet_primary_key_value
8
+
7
9
  def bullet_key
8
- "#{self.class}:#{bullet_primary_key_value}"
10
+ return "#{self.class}:" if respond_to?(:persisted?) && !persisted?
11
+
12
+ @bullet_key ||= "#{self.class}:#{bullet_primary_key_value}"
9
13
  end
10
14
 
11
15
  def bullet_primary_key_value
12
16
  return if respond_to?(:persisted?) && !persisted?
13
17
 
14
- if self.class.respond_to?(:primary_keys) && self.class.primary_keys
15
- primary_key = self.class.primary_keys
16
- elsif self.class.respond_to?(:primary_key) && self.class.primary_key
17
- primary_key = self.class.primary_key
18
- else
19
- primary_key = :id
20
- end
18
+ @bullet_primary_key_value ||=
19
+ begin
20
+ primary_key = self.class.try(:primary_keys) || self.class.try(:primary_key) || :id
21
21
 
22
- bullet_join_potential_composite_primary_key(primary_key)
22
+ bullet_join_potential_composite_primary_key(primary_key)
23
+ end
23
24
  end
24
25
 
25
26
  private
@@ -5,7 +5,8 @@ module Bullet
5
5
  module String
6
6
  refine ::String do
7
7
  def bullet_class_name
8
- sub(/:[^:]*?$/, '')
8
+ last_colon = self.rindex(':')
9
+ last_colon ? self[0...last_colon].dup : self.dup
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module Mongoid
5
+ def self.enable
6
+ require 'mongoid'
7
+ require 'rubygems'
8
+ ::Mongoid::Contextual::Mongo.class_eval do
9
+ alias_method :origin_first, :first
10
+ alias_method :origin_last, :last
11
+ alias_method :origin_each, :each
12
+ alias_method :origin_eager_load, :eager_load
13
+
14
+ %i[first last].each do |context|
15
+ default = Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('7.5') ? nil : {}
16
+ define_method(context) do |opts = default|
17
+ result = send(:"origin_#{context}", opts)
18
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
19
+ result
20
+ end
21
+ end
22
+
23
+ def each(&_block)
24
+ return to_enum unless block_given?
25
+
26
+ first_document = nil
27
+ document_count = 0
28
+
29
+ origin_each do |document|
30
+ document_count += 1
31
+
32
+ if document_count == 1
33
+ first_document = document
34
+ elsif document_count == 2
35
+ Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
36
+ yield(first_document)
37
+ first_document = nil
38
+ yield(document)
39
+ else
40
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
41
+ yield(document)
42
+ end
43
+ end
44
+
45
+ if document_count == 1
46
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
47
+ yield(first_document)
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ def eager_load(docs)
54
+ associations = criteria.inclusions.map(&:name)
55
+ docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
56
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
57
+ origin_eager_load(docs)
58
+ end
59
+ end
60
+
61
+ ::Mongoid::Association::Accessors.class_eval do
62
+ alias_method :origin_get_relation, :get_relation
63
+
64
+ def get_relation(name, association, object, reload = false)
65
+ result = origin_get_relation(name, association, object, reload)
66
+ Bullet::Detector::NPlusOneQuery.call_association(self, name) unless association.embedded?
67
+ result
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -7,7 +7,6 @@ using Bullet::Ext::Object
7
7
  module Bullet
8
8
  module StackTraceFilter
9
9
  VENDOR_PATH = '/vendor'
10
- IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
11
10
 
12
11
  # @param bullet_key[String] - use this to get stored call stack from call_stacks object.
13
12
  def caller_in_project(bullet_key = nil)
@@ -56,13 +55,12 @@ module Bullet
56
55
  def location_as_path(location)
57
56
  return location if location.is_a?(String)
58
57
 
59
- IS_RUBY_19 ? location : location.absolute_path.to_s
58
+ location.absolute_path.to_s
60
59
  end
61
60
 
62
61
  def select_caller_locations(bullet_key = nil)
63
- return caller.select { |caller_path| yield caller_path } if IS_RUBY_19
64
-
65
62
  call_stack = bullet_key ? call_stacks[bullet_key] : caller_locations
63
+
66
64
  call_stack.select { |location| yield location }
67
65
  end
68
66
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '8.0.0'
4
+ VERSION = '8.0.2'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/string/inflections'
3
4
  require 'active_support/core_ext/module/delegation'
4
5
  require 'set'
5
6
  require 'uniform_notifier'
@@ -63,7 +64,7 @@ module Bullet
63
64
  ].freeze
64
65
 
65
66
  def enable=(enable)
66
- @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
67
+ @enable = enable
67
68
 
68
69
  if enable?
69
70
  reset_safelist
@@ -89,15 +90,15 @@ module Bullet
89
90
  end
90
91
 
91
92
  def n_plus_one_query_enable?
92
- enable? && !!@n_plus_one_query_enable
93
+ enable? && (@n_plus_one_query_enable.nil? ? true : @n_plus_one_query_enable)
93
94
  end
94
95
 
95
96
  def unused_eager_loading_enable?
96
- enable? && !!@unused_eager_loading_enable
97
+ enable? && (@unused_eager_loading_enable.nil? ? true : @unused_eager_loading_enable)
97
98
  end
98
99
 
99
100
  def counter_cache_enable?
100
- enable? && !!@counter_cache_enable
101
+ enable? && (@counter_cache_enable.nil? ? true : @counter_cache_enable)
101
102
  end
102
103
 
103
104
  def stacktrace_includes
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0
4
+ version: 8.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-10 00:00:00.000000000 Z
10
+ date: 2025-04-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -76,6 +75,7 @@ files:
76
75
  - lib/bullet/mongoid6x.rb
77
76
  - lib/bullet/mongoid7x.rb
78
77
  - lib/bullet/mongoid8x.rb
78
+ - lib/bullet/mongoid9x.rb
79
79
  - lib/bullet/notification.rb
80
80
  - lib/bullet/notification/base.rb
81
81
  - lib/bullet/notification/counter_cache.rb
@@ -98,7 +98,6 @@ licenses:
98
98
  metadata:
99
99
  changelog_uri: https://github.com/flyerhzm/bullet/blob/main/CHANGELOG.md
100
100
  source_code_uri: https://github.com/flyerhzm/bullet
101
- post_install_message:
102
101
  rdoc_options: []
103
102
  require_paths:
104
103
  - lib
@@ -113,8 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
112
  - !ruby/object:Gem::Version
114
113
  version: 1.3.6
115
114
  requirements: []
116
- rubygems_version: 3.5.16
117
- signing_key:
115
+ rubygems_version: 3.6.2
118
116
  specification_version: 4
119
117
  summary: help to kill N+1 queries and unused eager loading.
120
118
  test_files: []