bullet 6.1.0 → 7.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +50 -0
  4. data/Gemfile.rails-6.0 +1 -1
  5. data/Gemfile.rails-6.1 +15 -0
  6. data/Gemfile.rails-7.0 +10 -0
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +38 -26
  9. data/lib/bullet/active_job.rb +5 -1
  10. data/lib/bullet/active_record41.rb +1 -0
  11. data/lib/bullet/active_record42.rb +1 -0
  12. data/lib/bullet/active_record5.rb +6 -4
  13. data/lib/bullet/active_record52.rb +28 -21
  14. data/lib/bullet/active_record60.rb +27 -20
  15. data/lib/bullet/active_record61.rb +274 -0
  16. data/lib/bullet/active_record70.rb +277 -0
  17. data/lib/bullet/bullet_xhr.js +18 -17
  18. data/lib/bullet/dependency.rb +16 -0
  19. data/lib/bullet/detector/base.rb +2 -1
  20. data/lib/bullet/detector/counter_cache.rb +2 -2
  21. data/lib/bullet/detector/n_plus_one_query.rb +5 -5
  22. data/lib/bullet/detector/unused_eager_loading.rb +2 -2
  23. data/lib/bullet/mongoid4x.rb +1 -1
  24. data/lib/bullet/mongoid5x.rb +1 -1
  25. data/lib/bullet/mongoid6x.rb +1 -1
  26. data/lib/bullet/mongoid7x.rb +32 -17
  27. data/lib/bullet/notification.rb +2 -1
  28. data/lib/bullet/rack.rb +28 -19
  29. data/lib/bullet/stack_trace_filter.rb +7 -9
  30. data/lib/bullet/version.rb +1 -1
  31. data/lib/bullet.rb +29 -28
  32. data/lib/generators/bullet/install_generator.rb +22 -25
  33. data/perf/benchmark.rb +4 -1
  34. data/spec/bullet/detector/counter_cache_spec.rb +1 -1
  35. data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -1
  36. data/spec/bullet/detector/unused_eager_loading_spec.rb +10 -10
  37. data/spec/bullet/ext/object_spec.rb +1 -1
  38. data/spec/bullet/notification/base_spec.rb +4 -4
  39. data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
  40. data/spec/bullet/rack_spec.rb +135 -9
  41. data/spec/bullet_spec.rb +15 -15
  42. data/spec/integration/active_record/association_spec.rb +73 -11
  43. data/spec/integration/counter_cache_spec.rb +4 -4
  44. data/spec/integration/mongoid/association_spec.rb +4 -4
  45. data/spec/models/attachment.rb +5 -0
  46. data/spec/models/deal.rb +5 -0
  47. data/spec/models/folder.rb +2 -1
  48. data/spec/models/group.rb +2 -1
  49. data/spec/models/page.rb +2 -1
  50. data/spec/models/post.rb +2 -0
  51. data/spec/models/role.rb +7 -0
  52. data/spec/models/submission.rb +1 -0
  53. data/spec/models/user.rb +2 -0
  54. data/spec/models/writer.rb +2 -1
  55. data/spec/spec_helper.rb +0 -2
  56. data/spec/support/mongo_seed.rb +1 -0
  57. data/spec/support/sqlite_seed.rb +38 -0
  58. data/test.sh +2 -0
  59. metadata +17 -7
  60. data/.travis.yml +0 -33
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module SaveWithBulletSupport
5
+ def _create_record(*)
6
+ super do
7
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
+ yield(self) if block_given?
9
+ end
10
+ end
11
+ end
12
+
13
+ module ActiveRecord
14
+ def self.enable
15
+ require 'active_record'
16
+ ::ActiveRecord::Base.extend(
17
+ Module.new do
18
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
19
+ result = super
20
+ if Bullet.start?
21
+ if result.is_a? Array
22
+ if result.size > 1
23
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
24
+ Bullet::Detector::CounterCache.add_possible_objects(result)
25
+ elsif result.size == 1
26
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
27
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
28
+ end
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
32
+ end
33
+ end
34
+ result
35
+ end
36
+ end
37
+ )
38
+
39
+ ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
40
+
41
+ ::ActiveRecord::Relation.prepend(
42
+ Module.new do
43
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
44
+ # if select only one object, then the only one object has impossible to cause N+1 query.
45
+ def records
46
+ result = super
47
+ if Bullet.start?
48
+ if result.first.class.name !~ /^HABTM_/
49
+ if result.size > 1
50
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
51
+ Bullet::Detector::CounterCache.add_possible_objects(result)
52
+ elsif result.size == 1
53
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
54
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
55
+ end
56
+ end
57
+ end
58
+ result
59
+ end
60
+ end
61
+ )
62
+
63
+ ::ActiveRecord::Associations::Preloader.prepend(
64
+ Module.new do
65
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
66
+ if Bullet.start?
67
+ records.compact!
68
+ if records.first.class.name !~ /^HABTM_/
69
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
70
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
71
+ end
72
+ end
73
+ super
74
+ end
75
+
76
+ def preloaders_for_reflection(reflection, records, scope)
77
+ if Bullet.start?
78
+ records.compact!
79
+ if records.first.class.name !~ /^HABTM_/
80
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
81
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, reflection.name)
82
+ end
83
+ end
84
+ super
85
+ end
86
+ end
87
+ )
88
+
89
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
90
+ Module.new do
91
+ def preloaded_records
92
+ if Bullet.start? && !defined?(@preloaded_records)
93
+ source_preloaders.each do |source_preloader|
94
+ reflection_name = source_preloader.send(:reflection).name
95
+ source_preloader.send(:owners).each do |owner|
96
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
97
+ end
98
+ end
99
+ end
100
+ super
101
+ end
102
+ end
103
+ )
104
+
105
+ ::ActiveRecord::Associations::JoinDependency.prepend(
106
+ Module.new do
107
+ def instantiate(result_set, strict_loading_value, &block)
108
+ @bullet_eager_loadings = {}
109
+ records = super
110
+
111
+ if Bullet.start?
112
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
113
+ objects = eager_loadings_hash.keys
114
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
115
+ objects,
116
+ eager_loadings_hash[objects.first].to_a
117
+ )
118
+ end
119
+ end
120
+ records
121
+ end
122
+
123
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
124
+ if Bullet.start?
125
+ unless ar_parent.nil?
126
+ parent.children.each do |node|
127
+ key = aliases.column_alias(node, node.primary_key)
128
+ id = row[key]
129
+ next unless id.nil?
130
+
131
+ associations = node.reflection.name
132
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
133
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
134
+ @bullet_eager_loadings[ar_parent.class] ||= {}
135
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
136
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
137
+ end
138
+ end
139
+ end
140
+
141
+ super
142
+ end
143
+
144
+ # call join associations
145
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
146
+ result = super
147
+
148
+ if Bullet.start?
149
+ associations = node.reflection.name
150
+ Bullet::Detector::Association.add_object_associations(record, associations)
151
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
152
+ @bullet_eager_loadings[record.class] ||= {}
153
+ @bullet_eager_loadings[record.class][record] ||= Set.new
154
+ @bullet_eager_loadings[record.class][record] << associations
155
+ end
156
+
157
+ result
158
+ end
159
+ end
160
+ )
161
+
162
+ ::ActiveRecord::Associations::Association.prepend(
163
+ Module.new do
164
+ def inversed_from(record)
165
+ if Bullet.start?
166
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
167
+ end
168
+ super
169
+ end
170
+ end
171
+ )
172
+
173
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
174
+ Module.new do
175
+ def load_target
176
+ records = super
177
+
178
+ if Bullet.start?
179
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
180
+ association = owner.association(reflection.through_reflection.name)
181
+ if association.loaded?
182
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
183
+ Array.wrap(association.target).each do |through_record|
184
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
185
+ end
186
+ end
187
+
188
+ if reflection.through_reflection != through_reflection
189
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
190
+ end
191
+ end
192
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
193
+ if records.first.class.name !~ /^HABTM_/
194
+ if records.size > 1
195
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
196
+ Bullet::Detector::CounterCache.add_possible_objects(records)
197
+ elsif records.size == 1
198
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
199
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
200
+ end
201
+ end
202
+ end
203
+ records
204
+ end
205
+
206
+ def empty?
207
+ if Bullet.start? && !reflection.has_cached_counter?
208
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
209
+ end
210
+ super
211
+ end
212
+
213
+ def include?(object)
214
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
215
+ super
216
+ end
217
+ end
218
+ )
219
+
220
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
221
+ Module.new do
222
+ # call has_one and belongs_to associations
223
+ def target
224
+ result = super()
225
+
226
+ if Bullet.start?
227
+ if owner.class.name !~ /^HABTM_/ && !@inversed
228
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
229
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
230
+ association = owner.association(reflection.through_reflection.name)
231
+ Array.wrap(association.target).each do |through_record|
232
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
233
+ end
234
+
235
+ if reflection.through_reflection != through_reflection
236
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
237
+ end
238
+ end
239
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
240
+
241
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
242
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
243
+ else
244
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
245
+ end
246
+ end
247
+ end
248
+ result
249
+ end
250
+ end
251
+ )
252
+
253
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
254
+ Module.new do
255
+ def empty?
256
+ result = super
257
+ if Bullet.start? && !reflection.has_cached_counter?
258
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
259
+ end
260
+ result
261
+ end
262
+
263
+ def count_records
264
+ result = reflection.has_cached_counter?
265
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
266
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
267
+ end
268
+ super
269
+ end
270
+ end
271
+ )
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bullet
4
+ module SaveWithBulletSupport
5
+ def _create_record(*)
6
+ super do
7
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
+ yield(self) if block_given?
9
+ end
10
+ end
11
+ end
12
+
13
+ module ActiveRecord
14
+ def self.enable
15
+ require 'active_record'
16
+ ::ActiveRecord::Base.extend(
17
+ Module.new do
18
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
19
+ result = super
20
+ if Bullet.start?
21
+ if result.is_a? Array
22
+ if result.size > 1
23
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
24
+ Bullet::Detector::CounterCache.add_possible_objects(result)
25
+ elsif result.size == 1
26
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
27
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
28
+ end
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
32
+ end
33
+ end
34
+ result
35
+ end
36
+ end
37
+ )
38
+
39
+ ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
40
+
41
+ ::ActiveRecord::Relation.prepend(
42
+ Module.new do
43
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
44
+ # if select only one object, then the only one object has impossible to cause N+1 query.
45
+ def records
46
+ result = super
47
+ if Bullet.start?
48
+ if result.first.class.name !~ /^HABTM_/
49
+ if result.size > 1
50
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
51
+ Bullet::Detector::CounterCache.add_possible_objects(result)
52
+ elsif result.size == 1
53
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
54
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
55
+ end
56
+ end
57
+ end
58
+ result
59
+ end
60
+ end
61
+ )
62
+
63
+ ::ActiveRecord::Associations::Preloader::Batch.prepend(
64
+ Module.new do
65
+ def call
66
+ if Bullet.start?
67
+ @preloaders.each do |preloader|
68
+ preloader.records.each { |record| Bullet::Detector::Association.add_object_associations(record, preloader.associations) }
69
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(preloader.records, preloader.associations)
70
+ end
71
+ end
72
+ super
73
+ end
74
+ end
75
+ )
76
+
77
+ ::ActiveRecord::Associations::Preloader::Branch.prepend(
78
+ Module.new do
79
+ def preloaders_for_reflection(reflection, reflection_records)
80
+ if Bullet.start?
81
+ reflection_records.compact!
82
+ if reflection_records.first.class.name !~ /^HABTM_/
83
+ reflection_records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
84
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
85
+ end
86
+ end
87
+ super
88
+ end
89
+ end
90
+ )
91
+
92
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
93
+ Module.new do
94
+ def preloaded_records
95
+ if Bullet.start? && !defined?(@preloaded_records)
96
+ source_preloaders.each do |source_preloader|
97
+ reflection_name = source_preloader.send(:reflection).name
98
+ source_preloader.send(:owners).each do |owner|
99
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
100
+ end
101
+ end
102
+ end
103
+ super
104
+ end
105
+ end
106
+ )
107
+
108
+ ::ActiveRecord::Associations::JoinDependency.prepend(
109
+ Module.new do
110
+ def instantiate(result_set, strict_loading_value, &block)
111
+ @bullet_eager_loadings = {}
112
+ records = super
113
+
114
+ if Bullet.start?
115
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
116
+ objects = eager_loadings_hash.keys
117
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
118
+ objects,
119
+ eager_loadings_hash[objects.first].to_a
120
+ )
121
+ end
122
+ end
123
+ records
124
+ end
125
+
126
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
127
+ if Bullet.start?
128
+ unless ar_parent.nil?
129
+ parent.children.each do |node|
130
+ key = aliases.column_alias(node, node.primary_key)
131
+ id = row[key]
132
+ next unless id.nil?
133
+
134
+ associations = node.reflection.name
135
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
136
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
137
+ @bullet_eager_loadings[ar_parent.class] ||= {}
138
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
139
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
140
+ end
141
+ end
142
+ end
143
+
144
+ super
145
+ end
146
+
147
+ # call join associations
148
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
149
+ result = super
150
+
151
+ if Bullet.start?
152
+ associations = node.reflection.name
153
+ Bullet::Detector::Association.add_object_associations(record, associations)
154
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
155
+ @bullet_eager_loadings[record.class] ||= {}
156
+ @bullet_eager_loadings[record.class][record] ||= Set.new
157
+ @bullet_eager_loadings[record.class][record] << associations
158
+ end
159
+
160
+ result
161
+ end
162
+ end
163
+ )
164
+
165
+ ::ActiveRecord::Associations::Association.prepend(
166
+ Module.new do
167
+ def inversed_from(record)
168
+ if Bullet.start?
169
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
170
+ end
171
+ super
172
+ end
173
+ end
174
+ )
175
+
176
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
177
+ Module.new do
178
+ def load_target
179
+ records = super
180
+
181
+ if Bullet.start?
182
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
183
+ association = owner.association(reflection.through_reflection.name)
184
+ if association.loaded?
185
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
186
+ Array.wrap(association.target).each do |through_record|
187
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
188
+ end
189
+ end
190
+
191
+ if reflection.through_reflection != through_reflection
192
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
193
+ end
194
+ end
195
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
196
+ if records.first.class.name !~ /^HABTM_/
197
+ if records.size > 1
198
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
199
+ Bullet::Detector::CounterCache.add_possible_objects(records)
200
+ elsif records.size == 1
201
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
202
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
203
+ end
204
+ end
205
+ end
206
+ records
207
+ end
208
+
209
+ def empty?
210
+ if Bullet.start? && !reflection.has_cached_counter?
211
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
212
+ end
213
+ super
214
+ end
215
+
216
+ def include?(object)
217
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
218
+ super
219
+ end
220
+ end
221
+ )
222
+
223
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
224
+ Module.new do
225
+ # call has_one and belongs_to associations
226
+ def reader
227
+ result = super
228
+
229
+ if Bullet.start?
230
+ if owner.class.name !~ /^HABTM_/
231
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
232
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
233
+ association = owner.association(reflection.through_reflection.name)
234
+ Array.wrap(association.target).each do |through_record|
235
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
236
+ end
237
+
238
+ if reflection.through_reflection != through_reflection
239
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
240
+ end
241
+ end
242
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
243
+
244
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
245
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
246
+ else
247
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
248
+ end
249
+ end
250
+ end
251
+ result
252
+ end
253
+ end
254
+ )
255
+
256
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
257
+ Module.new do
258
+ def empty?
259
+ result = super
260
+ if Bullet.start? && !reflection.has_cached_counter?
261
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
262
+ end
263
+ result
264
+ end
265
+
266
+ def count_records
267
+ result = reflection.has_cached_counter?
268
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
269
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
270
+ end
271
+ super
272
+ end
273
+ end
274
+ )
275
+ end
276
+ end
277
+ end
@@ -1,4 +1,4 @@
1
- (function() {
1
+ (function () {
2
2
  var oldOpen = window.XMLHttpRequest.prototype.open;
3
3
  var oldSend = window.XMLHttpRequest.prototype.send;
4
4
 
@@ -10,41 +10,42 @@
10
10
  if (isBulletInitiated()) return;
11
11
 
12
12
  function isBulletInitiated() {
13
- return oldOpen.name == 'bulletXHROpen' && oldSend.name == 'bulletXHRSend';
13
+ return oldOpen.name == "bulletXHROpen" && oldSend.name == "bulletXHRSend";
14
14
  }
15
15
  function bulletXHROpen(_, url) {
16
16
  this._storedUrl = url;
17
- return oldOpen.apply(this, arguments);
17
+ return Reflect.apply(oldOpen, this, arguments);
18
18
  }
19
19
  function bulletXHRSend() {
20
20
  if (this.onload) {
21
21
  this._storedOnload = this.onload;
22
22
  }
23
- this.addEventListener('load', bulletXHROnload);
24
- return oldSend.apply(this, arguments);
23
+ this.onload = null;
24
+ this.addEventListener("load", bulletXHROnload);
25
+ return Reflect.apply(oldSend, this, arguments);
25
26
  }
26
27
  function bulletXHROnload() {
27
28
  if (
28
- this._storedUrl.startsWith(window.location.protocol + '//' + window.location.host) ||
29
- !this._storedUrl.startsWith('http') // For relative paths
29
+ this._storedUrl.startsWith(window.location.protocol + "//" + window.location.host) ||
30
+ !this._storedUrl.startsWith("http") // For relative paths
30
31
  ) {
31
- var bulletFooterText = this.getResponseHeader('X-bullet-footer-text');
32
+ var bulletFooterText = this.getResponseHeader("X-bullet-footer-text");
32
33
  if (bulletFooterText) {
33
- setTimeout(() => {
34
- var oldHtml = document.getElementById('bullet-footer').innerHTML.split('<br>');
34
+ setTimeout(function () {
35
+ var oldHtml = document.querySelector("#bullet-footer").innerHTML.split("<br>");
35
36
  var header = oldHtml[0];
36
37
  oldHtml = oldHtml.slice(1, oldHtml.length);
37
38
  var newHtml = oldHtml.concat(JSON.parse(bulletFooterText));
38
39
  newHtml = newHtml.slice(newHtml.length - 10, newHtml.length); // rotate through 10 most recent
39
- document.getElementById('bullet-footer').innerHTML = `${header}<br>${newHtml.join('<br>')}`;
40
+ document.querySelector("#bullet-footer").innerHTML = `${header}<br>${newHtml.join("<br>")}`;
40
41
  }, 0);
41
42
  }
42
- var bulletConsoleText = this.getResponseHeader('X-bullet-console-text');
43
- if (bulletConsoleText && typeof console !== 'undefined' && console.log) {
44
- setTimeout(() => {
45
- JSON.parse(bulletConsoleText).forEach(message => {
43
+ var bulletConsoleText = this.getResponseHeader("X-bullet-console-text");
44
+ if (bulletConsoleText && typeof console !== "undefined" && console.log) {
45
+ setTimeout(function () {
46
+ JSON.parse(bulletConsoleText).forEach((message) => {
46
47
  if (console.groupCollapsed && console.groupEnd) {
47
- console.groupCollapsed('Uniform Notifier');
48
+ console.groupCollapsed("Uniform Notifier");
48
49
  console.log(message);
49
50
  console.groupEnd();
50
51
  } else {
@@ -55,7 +56,7 @@
55
56
  }
56
57
  }
57
58
  if (this._storedOnload) {
58
- return this._storedOnload.apply(this, arguments);
59
+ return Reflect.apply(this._storedOnload, this, arguments);
59
60
  }
60
61
  }
61
62
  window.XMLHttpRequest.prototype.open = bulletXHROpen;
@@ -27,6 +27,10 @@ module Bullet
27
27
  'active_record52'
28
28
  elsif active_record60?
29
29
  'active_record60'
30
+ elsif active_record61?
31
+ 'active_record61'
32
+ elsif active_record70?
33
+ 'active_record70'
30
34
  else
31
35
  raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
32
36
  end
@@ -62,6 +66,10 @@ module Bullet
62
66
  active_record? && ::ActiveRecord::VERSION::MAJOR == 6
63
67
  end
64
68
 
69
+ def active_record7?
70
+ active_record? && ::ActiveRecord::VERSION::MAJOR == 7
71
+ end
72
+
65
73
  def active_record40?
66
74
  active_record4? && ::ActiveRecord::VERSION::MINOR == 0
67
75
  end
@@ -90,6 +98,14 @@ module Bullet
90
98
  active_record6? && ::ActiveRecord::VERSION::MINOR == 0
91
99
  end
92
100
 
101
+ def active_record61?
102
+ active_record6? && ::ActiveRecord::VERSION::MINOR == 1
103
+ end
104
+
105
+ def active_record70?
106
+ active_record7? && ::ActiveRecord::VERSION::MINOR == 0
107
+ end
108
+
93
109
  def mongoid4x?
94
110
  mongoid? && ::Mongoid::VERSION =~ /\A4/
95
111
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Bullet
4
4
  module Detector
5
- class Base; end
5
+ class Base
6
+ end
6
7
  end
7
8
  end
@@ -20,7 +20,7 @@ module Bullet
20
20
  return unless Bullet.start?
21
21
  return unless Bullet.counter_cache_enable?
22
22
 
23
- objects = Array(object_or_objects)
23
+ objects = Array.wrap(object_or_objects)
24
24
  return if objects.map(&:bullet_primary_key_value).compact.empty?
25
25
 
26
26
  Bullet.debug(
@@ -54,7 +54,7 @@ module Bullet
54
54
  private
55
55
 
56
56
  def create_notification(klazz, associations)
57
- notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
57
+ notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:counter_cache, klazz)
58
58
 
59
59
  if notify_associations.present?
60
60
  notice = Bullet::Notification::CounterCache.new klazz, notify_associations