bullet 5.9.0 → 7.0.4

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