bullet 5.9.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 (71) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +72 -0
  4. data/Gemfile.rails-4.0 +1 -1
  5. data/Gemfile.rails-4.1 +1 -1
  6. data/Gemfile.rails-4.2 +1 -1
  7. data/Gemfile.rails-5.0 +1 -1
  8. data/Gemfile.rails-5.1 +1 -1
  9. data/Gemfile.rails-5.2 +1 -1
  10. data/Gemfile.rails-6.0 +15 -0
  11. data/Gemfile.rails-6.1 +15 -0
  12. data/Gemfile.rails-7.0 +10 -0
  13. data/MIT-LICENSE +1 -1
  14. data/README.md +59 -33
  15. data/lib/bullet/active_job.rb +13 -0
  16. data/lib/bullet/active_record4.rb +9 -32
  17. data/lib/bullet/active_record41.rb +8 -27
  18. data/lib/bullet/active_record42.rb +9 -24
  19. data/lib/bullet/active_record5.rb +190 -179
  20. data/lib/bullet/active_record52.rb +184 -169
  21. data/lib/bullet/active_record60.rb +274 -0
  22. data/lib/bullet/active_record61.rb +274 -0
  23. data/lib/bullet/active_record70.rb +277 -0
  24. data/lib/bullet/bullet_xhr.js +64 -0
  25. data/lib/bullet/dependency.rb +60 -36
  26. data/lib/bullet/detector/association.rb +26 -20
  27. data/lib/bullet/detector/counter_cache.rb +15 -11
  28. data/lib/bullet/detector/n_plus_one_query.rb +24 -14
  29. data/lib/bullet/detector/unused_eager_loading.rb +8 -5
  30. data/lib/bullet/ext/object.rb +4 -2
  31. data/lib/bullet/mongoid4x.rb +3 -7
  32. data/lib/bullet/mongoid5x.rb +3 -7
  33. data/lib/bullet/mongoid6x.rb +3 -7
  34. data/lib/bullet/mongoid7x.rb +34 -23
  35. data/lib/bullet/notification/base.rb +14 -18
  36. data/lib/bullet/notification/n_plus_one_query.rb +2 -4
  37. data/lib/bullet/notification/unused_eager_loading.rb +2 -4
  38. data/lib/bullet/notification.rb +2 -1
  39. data/lib/bullet/rack.rb +55 -27
  40. data/lib/bullet/stack_trace_filter.rb +11 -19
  41. data/lib/bullet/version.rb +1 -1
  42. data/lib/bullet.rb +68 -42
  43. data/lib/generators/bullet/install_generator.rb +22 -23
  44. data/perf/benchmark.rb +11 -14
  45. data/spec/bullet/detector/counter_cache_spec.rb +6 -6
  46. data/spec/bullet/detector/n_plus_one_query_spec.rb +8 -4
  47. data/spec/bullet/detector/unused_eager_loading_spec.rb +25 -8
  48. data/spec/bullet/ext/object_spec.rb +10 -5
  49. data/spec/bullet/notification/base_spec.rb +5 -7
  50. data/spec/bullet/notification/n_plus_one_query_spec.rb +16 -3
  51. data/spec/bullet/notification/unused_eager_loading_spec.rb +5 -1
  52. data/spec/bullet/rack_spec.rb +161 -11
  53. data/spec/bullet/registry/association_spec.rb +2 -2
  54. data/spec/bullet/registry/base_spec.rb +1 -1
  55. data/spec/bullet_spec.rb +25 -44
  56. data/spec/integration/active_record/association_spec.rb +115 -144
  57. data/spec/integration/counter_cache_spec.rb +14 -34
  58. data/spec/integration/mongoid/association_spec.rb +19 -33
  59. data/spec/models/attachment.rb +5 -0
  60. data/spec/models/deal.rb +5 -0
  61. data/spec/models/post.rb +2 -0
  62. data/spec/models/role.rb +7 -0
  63. data/spec/models/submission.rb +1 -0
  64. data/spec/models/user.rb +2 -0
  65. data/spec/spec_helper.rb +4 -10
  66. data/spec/support/bullet_ext.rb +8 -9
  67. data/spec/support/mongo_seed.rb +3 -16
  68. data/spec/support/sqlite_seed.rb +38 -0
  69. data/test.sh +3 -0
  70. metadata +21 -8
  71. data/.travis.yml +0 -12
@@ -13,220 +13,235 @@ 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)
153
- end
154
-
155
- if reflection.through_reflection != through_reflection
156
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_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
+ association = owner.association(reflection.through_reflection.name)
154
+ if association.loaded?
155
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
156
+ Array.wrap(association.target).each do |through_record|
157
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
158
+ end
159
+ end
160
+
161
+ if reflection.through_reflection != through_reflection
162
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
163
+ end
157
164
  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)
165
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
166
+ if records.first.class.name !~ /^HABTM_/
167
+ if records.size > 1
168
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
169
+ Bullet::Detector::CounterCache.add_possible_objects(records)
170
+ elsif records.size == 1
171
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
172
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
173
+ end
167
174
  end
168
175
  end
176
+ records
169
177
  end
170
- records
171
- end
172
178
 
173
- def empty?
174
- if Bullet.start? && !reflection.has_cached_counter?
175
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
179
+ def empty?
180
+ if Bullet.start? && !reflection.has_cached_counter?
181
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
182
+ end
183
+ super
176
184
  end
177
- super
178
- end
179
185
 
180
- def include?(object)
181
- if Bullet.start?
182
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
186
+ def include?(object)
187
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
188
+ super
183
189
  end
184
- super
185
190
  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
191
+ )
192
+
193
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
194
+ Module.new do
195
+ # call has_one and belongs_to associations
196
+ def target
197
+ result = super()
198
+
199
+ if Bullet.start?
200
+ if owner.class.name !~ /^HABTM_/ && !@inversed
201
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
202
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
203
+ association = owner.association reflection.through_reflection.name
204
+ Array.wrap(association.target).each do |through_record|
205
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
206
+ end
207
+
208
+ if reflection.through_reflection != through_reflection
209
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
210
+ end
211
+ end
212
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
213
+
214
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
215
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
216
+ else
217
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
218
+ end
199
219
  end
200
220
  end
221
+ result
201
222
  end
202
- result
203
223
  end
204
- end)
224
+ )
205
225
 
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)
226
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
227
+ Module.new do
228
+ def empty?
229
+ result = super
230
+ if Bullet.start? && !reflection.has_cached_counter?
231
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
232
+ end
233
+ result
211
234
  end
212
- result
213
- end
214
235
 
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)
236
+ def count_records
237
+ result = reflection.has_cached_counter?
238
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
239
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
240
+ end
241
+ super
219
242
  end
220
- super
221
- end
222
- end)
223
-
224
- ::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
225
- def writer(record)
226
- Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
227
- super
228
243
  end
229
- end)
244
+ )
230
245
  end
231
246
  end
232
247
  end