bullet 8.0.1 → 8.0.4

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: eb8c8d2264141849784cb8a103b9521a8fad5ba11eba1daa9a5a576f02c2bcc6
4
- data.tar.gz: e9b5a2a2f123ad3e84007407123103535844dcde3c7e9f3da304eb019d55f96d
3
+ metadata.gz: a61908a92c8bc22bba69f9ea56946f1b6b2abd320e392e7539a3a6024b939b0c
4
+ data.tar.gz: 1fc844bc72f0e9e4c046590ef3f981180ce0beb5e32fab2217592d21e3456705
5
5
  SHA512:
6
- metadata.gz: bc9c11edab1b705ba7f51f63bf0315c94d5c3f4178684c42da4042adeef52da7570939a91556301dcb076a08a9dc816b89cd38c8a5399c7a8fe81baf2562ca01
7
- data.tar.gz: aa0cc8a93443e8fa17582ac89dca1eba8714080ebab2d0199b0a298f77ccf3f0a019b457615e18e6dd8827f3573e0fe657d9ada3c36d6e8cd588e2b2c4a4090f
6
+ metadata.gz: 456c8c81a574243cda8ad3a3bb728b0ae97f169434e1608158a72967b8f9a6e7153939fcb2ce7c9d9e363921e918871e6a8b3ed7c374a45bc910170a4cb3e779
7
+ data.tar.gz: 3ebfc6724a13326f9d2286506727edb5d47b66b01253fbc48a2407d9e7f1c6c3f8ac700d7405024fcc86766a9fd65d170d0eea0678ffd1af3c60dd90b0c82b06
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## Next Release
2
2
 
3
+ ## 8.0.4 (04/18/2024)
4
+
5
+ * Insert bullet middleware before `ContentSecurityPolicy`
6
+ * Support url query `skip_html_injection=true`
7
+ * Mark object as impossible after updating inversed
8
+
9
+ ## 8.0.3 (04/04/2025)
10
+
11
+ * Update non persisted `inversed_objects`
12
+
13
+ ## 8.0.2 (04/02/2025)
14
+
15
+ * Do not cache `bullet_key` if object is not persisted
16
+
3
17
  ## 8.0.1 (02/10/2025)
4
18
 
5
19
  * Update benchmark to use sqlite
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
@@ -194,6 +192,11 @@ see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/u
194
192
 
195
193
  Growl support is dropped from uniform_notifier 1.16.0, if you still want it, please use uniform_notifier 1.15.0.
196
194
 
195
+ ## URL query control
196
+
197
+ You can add the URL query parameter `skip_html_injection` to make the current HTML request behave as if `Bullet.skip_html_injection` is enabled,
198
+ e.g. `http://localhost:3000/posts?skip_html_injection=true`
199
+
197
200
  ## Important
198
201
 
199
202
  If you find Bullet does not work for you, *please disable your browser's cache*.
@@ -240,7 +243,7 @@ If your application generates a Content-Security-Policy via a separate middlewar
240
243
 
241
244
  ### Run in tests
242
245
 
243
- First you need to enable Bullet in test environment.
246
+ First you need to enable Bullet in the test environment.
244
247
 
245
248
  ```ruby
246
249
  # config/environments/test.rb
@@ -251,11 +254,13 @@ config.after_initialize do
251
254
  end
252
255
  ```
253
256
 
254
- Then wrap each test in Bullet api.
257
+ Then wrap each test in the Bullet api.
258
+
259
+ With RSpec:
255
260
 
256
261
  ```ruby
257
262
  # spec/rails_helper.rb
258
- if Bullet.enable?
263
+ RSpec.configure do |config|
259
264
  config.before(:each) do
260
265
  Bullet.start_request
261
266
  end
@@ -267,6 +272,26 @@ if Bullet.enable?
267
272
  end
268
273
  ```
269
274
 
275
+ With Minitest:
276
+
277
+ ```ruby
278
+ # test/test_helper.rb
279
+ module ActiveSupport
280
+ class TestCase
281
+ def before_setup
282
+ Bullet.start_request
283
+ super
284
+ end
285
+
286
+ def after_teardown
287
+ super
288
+ Bullet.perform_out_of_channel_notifications if Bullet.notification?
289
+ Bullet.end_request
290
+ end
291
+ end
292
+ end
293
+ ```
294
+
270
295
  ## Debug Mode
271
296
 
272
297
  Bullet outputs some details info, to enable debug mode, set
@@ -49,7 +49,10 @@ module Bullet
49
49
 
50
50
  ::ActiveRecord::Persistence.class_eval do
51
51
  def _create_record_with_bullet(*args)
52
- _create_record_without_bullet(*args).tap { Bullet::Detector::NPlusOneQuery.add_impossible_object(self) }
52
+ _create_record_without_bullet(*args).tap do
53
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
54
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
55
+ end
53
56
  end
54
57
  alias_method_chain :_create_record, :bullet
55
58
  end
@@ -52,7 +52,10 @@ module Bullet
52
52
 
53
53
  ::ActiveRecord::Persistence.class_eval do
54
54
  def _create_record_with_bullet(*args)
55
- _create_record_without_bullet(*args).tap { Bullet::Detector::NPlusOneQuery.add_impossible_object(self) }
55
+ _create_record_without_bullet(*args).tap do
56
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
57
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
58
+ end
56
59
  end
57
60
  alias_method_chain :_create_record, :bullet
58
61
  end
@@ -45,7 +45,10 @@ module Bullet
45
45
 
46
46
  ::ActiveRecord::Persistence.class_eval do
47
47
  def _create_record_with_bullet(*args)
48
- _create_record_without_bullet(*args).tap { Bullet::Detector::NPlusOneQuery.add_impossible_object(self) }
48
+ _create_record_without_bullet(*args).tap do
49
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
50
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
51
+ end
49
52
  end
50
53
  alias_method_chain :_create_record, :bullet
51
54
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -4,6 +4,7 @@ module Bullet
4
4
  module SaveWithBulletSupport
5
5
  def _create_record(*)
6
6
  super do
7
+ Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
7
8
  Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
9
  yield(self) if block_given?
9
10
  end
@@ -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
@@ -67,13 +67,23 @@ module Bullet
67
67
  def add_inversed_object(object, association)
68
68
  return unless Bullet.start?
69
69
  return unless Bullet.n_plus_one_query_enable?
70
- return unless object.bullet_primary_key_value
71
70
 
71
+ object_key = object.bullet_primary_key_value ? object.bullet_key : object.object_id
72
72
  Bullet.debug(
73
73
  'Detector::NPlusOneQuery#add_inversed_object',
74
- "object: #{object.bullet_key}, association: #{association}"
74
+ "object: #{object_key}, association: #{association}"
75
75
  )
76
- inversed_objects.add object.bullet_key, association
76
+ inversed_objects.add object_key, association
77
+ end
78
+
79
+ def update_inversed_object(object)
80
+ if inversed_objects&.key?(object.object_id)
81
+ Bullet.debug(
82
+ 'Detector::NPlusOneQuery#update_inversed_object',
83
+ "object from #{object.object_id} to #{object.bullet_key}"
84
+ )
85
+ inversed_objects.add(object.bullet_key, inversed_objects[object.object_id].to_a)
86
+ end
77
87
  end
78
88
 
79
89
  # decide whether the object.associations is unpreloaded or not.
@@ -7,17 +7,20 @@ module Bullet
7
7
  attr_writer :bullet_key, :bullet_primary_key_value
8
8
 
9
9
  def bullet_key
10
+ return "#{self.class}:" if respond_to?(:persisted?) && !persisted?
11
+
10
12
  @bullet_key ||= "#{self.class}:#{bullet_primary_key_value}"
11
13
  end
12
14
 
13
15
  def bullet_primary_key_value
14
- @bullet_primary_key_value ||= begin
15
- return if respond_to?(:persisted?) && !persisted?
16
+ return if respond_to?(:persisted?) && !persisted?
16
17
 
17
- primary_key = self.class.try(:primary_keys) || self.class.try(:primary_key) || :id
18
+ @bullet_primary_key_value ||=
19
+ begin
20
+ primary_key = self.class.try(:primary_keys) || self.class.try(:primary_key) || :id
18
21
 
19
- bullet_join_potential_composite_primary_key(primary_key)
20
- end
22
+ bullet_join_potential_composite_primary_key(primary_key)
23
+ end
21
24
  end
22
25
 
23
26
  private
@@ -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
data/lib/bullet/rack.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rack/request'
4
+ require 'json'
5
+
3
6
  module Bullet
4
7
  class Rack
5
8
  include Dependency
@@ -19,7 +22,8 @@ module Bullet
19
22
  response_body = nil
20
23
 
21
24
  if Bullet.notification? || Bullet.always_append_html_body
22
- if Bullet.inject_into_page? && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
25
+ request = ::Rack::Request.new(env)
26
+ if Bullet.inject_into_page? && !skip_html_injection?(request) && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
23
27
  if html_request?(headers, response)
24
28
  response_body = response_body(response)
25
29
 
@@ -73,8 +77,23 @@ module Bullet
73
77
  # Many proxy applications such as Nginx and AWS ELB limit
74
78
  # the size a header to 8KB, so truncate the list of reports to
75
79
  # be under that limit
76
- header_array.pop while header_array.to_json.length > 8 * 1024
77
- headers[header_name] = header_array.to_json
80
+ header_array.pop while JSON.generate(header_array).length > 8 * 1024
81
+ headers[header_name] = JSON.generate(header_array)
82
+ end
83
+
84
+ def skip_html_injection?(request)
85
+ query_string = request.env['QUERY_STRING']
86
+ return false if query_string.nil? || query_string.empty?
87
+
88
+ if defined?(Rack::QueryParser)
89
+ parser = Rack::QueryParser.new
90
+ params = parser.parse_nested_query(query_string)
91
+ else
92
+ # compatible with rack 1.x,
93
+ # remove it after dropping rails 4.2 suppport
94
+ params = Rack::Utils.parse_nested_query(query_string)
95
+ end
96
+ params['skip_html_injection'] == 'true'
78
97
  end
79
98
 
80
99
  def file?(headers)
@@ -35,7 +35,11 @@ module Bullet
35
35
  end
36
36
 
37
37
  def include?(key, value)
38
- !@registry[key].nil? && @registry[key].include?(value)
38
+ key?(key) && @registry[key].include?(value)
39
+ end
40
+
41
+ def key?(key)
42
+ @registry.key?(key)
39
43
  end
40
44
  end
41
45
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '8.0.1'
4
+ VERSION = '8.0.4'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -24,7 +24,7 @@ module Bullet
24
24
  if defined?(Rails::Railtie)
25
25
  class BulletRailtie < Rails::Railtie
26
26
  initializer 'bullet.configure_rails_initialization' do |app|
27
- if defined?(ActionDispatch::ContentSecurityPolicy::Middleware) && Rails.application.config.content_security_policy
27
+ if defined?(ActionDispatch::ContentSecurityPolicy::Middleware)
28
28
  app.middleware.insert_before ActionDispatch::ContentSecurityPolicy::Middleware, Bullet::Rack
29
29
  else
30
30
  app.middleware.use Bullet::Rack
@@ -64,7 +64,7 @@ module Bullet
64
64
  ].freeze
65
65
 
66
66
  def enable=(enable)
67
- @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
67
+ @enable = enable
68
68
 
69
69
  if enable?
70
70
  reset_safelist
@@ -90,15 +90,15 @@ module Bullet
90
90
  end
91
91
 
92
92
  def n_plus_one_query_enable?
93
- enable? && !!@n_plus_one_query_enable
93
+ enable? && (@n_plus_one_query_enable.nil? ? true : @n_plus_one_query_enable)
94
94
  end
95
95
 
96
96
  def unused_eager_loading_enable?
97
- enable? && !!@unused_eager_loading_enable
97
+ enable? && (@unused_eager_loading_enable.nil? ? true : @unused_eager_loading_enable)
98
98
  end
99
99
 
100
100
  def counter_cache_enable?
101
- enable? && !!@counter_cache_enable
101
+ enable? && (@counter_cache_enable.nil? ? true : @counter_cache_enable)
102
102
  end
103
103
 
104
104
  def stacktrace_includes
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.1
4
+ version: 8.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-10 00:00:00.000000000 Z
10
+ date: 2025-04-18 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -75,6 +75,7 @@ files:
75
75
  - lib/bullet/mongoid6x.rb
76
76
  - lib/bullet/mongoid7x.rb
77
77
  - lib/bullet/mongoid8x.rb
78
+ - lib/bullet/mongoid9x.rb
78
79
  - lib/bullet/notification.rb
79
80
  - lib/bullet/notification/base.rb
80
81
  - lib/bullet/notification/counter_cache.rb