bullet 6.0.2 → 7.0.3

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