bullet 6.0.2 → 7.0.3

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +52 -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/README.md +39 -25
  8. data/lib/bullet/active_job.rb +1 -3
  9. data/lib/bullet/active_record4.rb +9 -23
  10. data/lib/bullet/active_record41.rb +8 -19
  11. data/lib/bullet/active_record42.rb +9 -16
  12. data/lib/bullet/active_record5.rb +188 -170
  13. data/lib/bullet/active_record52.rb +182 -162
  14. data/lib/bullet/active_record60.rb +191 -178
  15. data/lib/bullet/active_record61.rb +272 -0
  16. data/lib/bullet/active_record70.rb +275 -0
  17. data/lib/bullet/bullet_xhr.js +18 -23
  18. data/lib/bullet/dependency.rb +52 -34
  19. data/lib/bullet/detector/association.rb +24 -18
  20. data/lib/bullet/detector/counter_cache.rb +12 -8
  21. data/lib/bullet/detector/n_plus_one_query.rb +20 -10
  22. data/lib/bullet/detector/unused_eager_loading.rb +7 -4
  23. data/lib/bullet/mongoid4x.rb +3 -7
  24. data/lib/bullet/mongoid5x.rb +3 -7
  25. data/lib/bullet/mongoid6x.rb +3 -7
  26. data/lib/bullet/mongoid7x.rb +26 -13
  27. data/lib/bullet/notification/base.rb +14 -18
  28. data/lib/bullet/notification/n_plus_one_query.rb +2 -4
  29. data/lib/bullet/notification/unused_eager_loading.rb +2 -4
  30. data/lib/bullet/notification.rb +2 -1
  31. data/lib/bullet/rack.rb +28 -17
  32. data/lib/bullet/stack_trace_filter.rb +10 -17
  33. data/lib/bullet/version.rb +1 -1
  34. data/lib/bullet.rb +52 -42
  35. data/lib/generators/bullet/install_generator.rb +22 -23
  36. data/perf/benchmark.rb +11 -14
  37. data/spec/bullet/detector/counter_cache_spec.rb +6 -6
  38. data/spec/bullet/detector/n_plus_one_query_spec.rb +8 -4
  39. data/spec/bullet/detector/unused_eager_loading_spec.rb +25 -8
  40. data/spec/bullet/ext/object_spec.rb +1 -1
  41. data/spec/bullet/notification/base_spec.rb +5 -7
  42. data/spec/bullet/notification/n_plus_one_query_spec.rb +16 -3
  43. data/spec/bullet/notification/unused_eager_loading_spec.rb +5 -1
  44. data/spec/bullet/rack_spec.rb +154 -13
  45. data/spec/bullet/registry/association_spec.rb +2 -2
  46. data/spec/bullet/registry/base_spec.rb +1 -1
  47. data/spec/bullet_spec.rb +25 -44
  48. data/spec/integration/active_record/association_spec.rb +104 -130
  49. data/spec/integration/counter_cache_spec.rb +14 -34
  50. data/spec/integration/mongoid/association_spec.rb +19 -33
  51. data/spec/models/attachment.rb +5 -0
  52. data/spec/models/deal.rb +5 -0
  53. data/spec/models/post.rb +2 -0
  54. data/spec/models/role.rb +7 -0
  55. data/spec/models/submission.rb +1 -0
  56. data/spec/models/user.rb +2 -0
  57. data/spec/spec_helper.rb +4 -10
  58. data/spec/support/bullet_ext.rb +8 -9
  59. data/spec/support/mongo_seed.rb +3 -16
  60. data/spec/support/sqlite_seed.rb +38 -0
  61. data/test.sh +2 -0
  62. metadata +17 -7
  63. data/.travis.yml +0 -31
@@ -13,213 +13,233 @@ module Bullet
13
13
  module ActiveRecord
14
14
  def self.enable
15
15
  require 'active_record'
16
- ::ActiveRecord::Base.extend(Module.new do
17
- def find_by_sql(sql, binds = [], preparable: nil, &block)
18
- result = super
19
- if Bullet.start?
20
- if result.is_a? Array
21
- if result.size > 1
22
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
23
- Bullet::Detector::CounterCache.add_possible_objects(result)
24
- elsif result.size == 1
25
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
26
- Bullet::Detector::CounterCache.add_impossible_object(result.first)
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)
27
32
  end
28
- elsif result.is_a? ::ActiveRecord::Base
29
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
30
- Bullet::Detector::CounterCache.add_impossible_object(result)
31
33
  end
34
+ result
32
35
  end
33
- result
34
36
  end
35
- end)
37
+ )
36
38
 
37
39
  ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
38
40
 
39
- ::ActiveRecord::Relation.prepend(Module.new do
40
- # if select a collection of objects, then these objects have possible to cause N+1 query.
41
- # if select only one object, then the only one object has impossible to cause N+1 query.
42
- def records
43
- result = super
44
- if Bullet.start?
45
- if result.first.class.name !~ /^HABTM_/
46
- if result.size > 1
47
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
48
- Bullet::Detector::CounterCache.add_possible_objects(result)
49
- elsif result.size == 1
50
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
51
- Bullet::Detector::CounterCache.add_impossible_object(result.first)
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
52
56
  end
53
57
  end
58
+ result
54
59
  end
55
- result
56
60
  end
57
- end)
58
-
59
- ::ActiveRecord::Associations::Preloader.prepend(Module.new do
60
- def preloaders_for_one(association, records, scope)
61
- if Bullet.start?
62
- records.compact!
63
- if records.first.class.name !~ /^HABTM_/
64
- records.each do |record|
65
- Bullet::Detector::Association.add_object_associations(record, association)
61
+ )
62
+
63
+ ::ActiveRecord::Associations::Preloader.prepend(
64
+ Module.new do
65
+ def preloaders_for_one(association, records, scope)
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)
66
71
  end
67
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
68
72
  end
73
+ super
69
74
  end
70
- super
71
75
  end
72
- end)
73
-
74
- ::ActiveRecord::FinderMethods.prepend(Module.new do
75
- # add includes in scope
76
- def find_with_associations
77
- return super { |r| yield r } if block_given?
78
-
79
- records = super
80
- if Bullet.start?
81
- associations = (eager_load_values + includes_values).uniq
82
- records.each do |record|
83
- Bullet::Detector::Association.add_object_associations(record, associations)
76
+ )
77
+
78
+ ::ActiveRecord::Associations::JoinDependency.prepend(
79
+ Module.new do
80
+ def instantiate(result_set, &block)
81
+ @bullet_eager_loadings = {}
82
+ records = super
83
+
84
+ if Bullet.start?
85
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
86
+ objects = eager_loadings_hash.keys
87
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
88
+ objects,
89
+ eager_loadings_hash[objects.first].to_a
90
+ )
91
+ end
84
92
  end
85
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
93
+ records
86
94
  end
87
- records
88
- end
89
- end)
90
-
91
- ::ActiveRecord::Associations::JoinDependency.prepend(Module.new do
92
- def instantiate(result_set, &block)
93
- @bullet_eager_loadings = {}
94
- records = super
95
95
 
96
- if Bullet.start?
97
- @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
98
- objects = eager_loadings_hash.keys
99
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
96
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
97
+ if Bullet.start?
98
+ unless ar_parent.nil?
99
+ parent.children.each do |node|
100
+ key = aliases.column_alias(node, node.primary_key)
101
+ id = row[key]
102
+ next unless id.nil?
103
+
104
+ associations = node.reflection.name
105
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
106
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
107
+ @bullet_eager_loadings[ar_parent.class] ||= {}
108
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
109
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
110
+ end
111
+ end
100
112
  end
113
+
114
+ super
101
115
  end
102
- records
103
- end
104
116
 
105
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
106
- if Bullet.start?
107
- unless ar_parent.nil?
108
- parent.children.each do |node|
109
- key = aliases.column_alias(node, node.primary_key)
110
- id = row[key]
111
- next unless id.nil?
112
-
113
- associations = node.reflection.name
114
- Bullet::Detector::Association.add_object_associations(ar_parent, associations)
115
- Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
116
- @bullet_eager_loadings[ar_parent.class] ||= {}
117
- @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
118
- @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
119
- end
117
+ # call join associations
118
+ def construct_model(record, node, row, model_cache, id, aliases)
119
+ result = super
120
+
121
+ if Bullet.start?
122
+ associations = node.reflection.name
123
+ Bullet::Detector::Association.add_object_associations(record, associations)
124
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
125
+ @bullet_eager_loadings[record.class] ||= {}
126
+ @bullet_eager_loadings[record.class][record] ||= Set.new
127
+ @bullet_eager_loadings[record.class][record] << associations
120
128
  end
121
- end
122
129
 
123
- super
130
+ result
131
+ end
124
132
  end
133
+ )
125
134
 
126
- # call join associations
127
- def construct_model(record, node, row, model_cache, id, aliases)
128
- result = super
129
-
130
- if Bullet.start?
131
- associations = node.reflection.name
132
- Bullet::Detector::Association.add_object_associations(record, associations)
133
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
134
- @bullet_eager_loadings[record.class] ||= {}
135
- @bullet_eager_loadings[record.class][record] ||= Set.new
136
- @bullet_eager_loadings[record.class][record] << associations
135
+ ::ActiveRecord::Associations::Association.prepend(
136
+ Module.new do
137
+ def inversed_from(record)
138
+ if Bullet.start?
139
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
140
+ end
141
+ super
137
142
  end
138
-
139
- result
140
143
  end
141
- end)
142
-
143
- ::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new do
144
- def load_target
145
- records = super
146
-
147
- if Bullet.start?
148
- if is_a? ::ActiveRecord::Associations::ThroughAssociation
149
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
150
- association = owner.association reflection.through_reflection.name
151
- Array(association.target).each do |through_record|
152
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
144
+ )
145
+
146
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
147
+ Module.new do
148
+ def load_target
149
+ records = super
150
+
151
+ if Bullet.start?
152
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
153
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
154
+ association = owner.association reflection.through_reflection.name
155
+ Array.wrap(association.target).each do |through_record|
156
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
157
+ end
158
+
159
+ if reflection.through_reflection != through_reflection
160
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
161
+ end
153
162
  end
154
-
155
- if reflection.through_reflection != through_reflection
156
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
157
- end
158
- end
159
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
160
- if records.first.class.name !~ /^HABTM_/
161
- if records.size > 1
162
- Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
163
- Bullet::Detector::CounterCache.add_possible_objects(records)
164
- elsif records.size == 1
165
- Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
166
- Bullet::Detector::CounterCache.add_impossible_object(records.first)
163
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
164
+ if records.first.class.name !~ /^HABTM_/
165
+ if records.size > 1
166
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
167
+ Bullet::Detector::CounterCache.add_possible_objects(records)
168
+ elsif records.size == 1
169
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
170
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
171
+ end
167
172
  end
168
173
  end
174
+ records
169
175
  end
170
- records
171
- end
172
176
 
173
- def empty?
174
- if Bullet.start? && !reflection.has_cached_counter?
175
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
177
+ def empty?
178
+ if Bullet.start? && !reflection.has_cached_counter?
179
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
180
+ end
181
+ super
176
182
  end
177
- super
178
- end
179
183
 
180
- def include?(object)
181
- if Bullet.start?
182
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
184
+ def include?(object)
185
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
186
+ super
183
187
  end
184
- super
185
188
  end
186
- end)
187
-
188
- ::ActiveRecord::Associations::SingularAssociation.prepend(Module.new do
189
- # call has_one and belongs_to associations
190
- def target
191
- result = super()
192
- if Bullet.start?
193
- if owner.class.name !~ /^HABTM_/ && !@inversed
194
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
195
- if Bullet::Detector::NPlusOneQuery.impossible?(owner)
196
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
197
- else
198
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
189
+ )
190
+
191
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
192
+ Module.new do
193
+ # call has_one and belongs_to associations
194
+ def target
195
+ result = super()
196
+
197
+ if Bullet.start?
198
+ if owner.class.name !~ /^HABTM_/ && !@inversed
199
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
200
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
201
+ association = owner.association reflection.through_reflection.name
202
+ Array.wrap(association.target).each do |through_record|
203
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
204
+ end
205
+
206
+ if reflection.through_reflection != through_reflection
207
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
208
+ end
209
+ end
210
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
211
+
212
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
213
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
214
+ else
215
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
216
+ end
199
217
  end
200
218
  end
219
+ result
201
220
  end
202
- result
203
221
  end
204
- end)
222
+ )
205
223
 
206
- ::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new do
207
- def empty?
208
- result = super
209
- if Bullet.start? && !reflection.has_cached_counter?
210
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
224
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
225
+ Module.new do
226
+ def empty?
227
+ result = super
228
+ if Bullet.start? && !reflection.has_cached_counter?
229
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
230
+ end
231
+ result
211
232
  end
212
- result
213
- end
214
233
 
215
- def count_records
216
- result = reflection.has_cached_counter?
217
- if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
218
- Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
234
+ def count_records
235
+ result = reflection.has_cached_counter?
236
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
237
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
238
+ end
239
+ super
219
240
  end
220
- super
221
241
  end
222
- end)
242
+ )
223
243
  end
224
244
  end
225
245
  end