bullet 5.5.0 → 5.8.0

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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -5
  3. data/CHANGELOG.md +31 -1
  4. data/Gemfile +6 -1
  5. data/Gemfile.rails-5.0 +1 -1
  6. data/Gemfile.rails-5.1 +15 -0
  7. data/Gemfile.rails-5.2 +15 -0
  8. data/Guardfile +2 -2
  9. data/Hacking.md +1 -1
  10. data/README.md +20 -23
  11. data/Rakefile +20 -21
  12. data/bullet.gemspec +20 -15
  13. data/lib/bullet/active_record4.rb +18 -14
  14. data/lib/bullet/active_record41.rb +16 -14
  15. data/lib/bullet/active_record42.rb +24 -22
  16. data/lib/bullet/active_record5.rb +102 -91
  17. data/lib/bullet/active_record52.rb +232 -0
  18. data/lib/bullet/dependency.rb +9 -1
  19. data/lib/bullet/detector/association.rb +17 -14
  20. data/lib/bullet/detector/base.rb +2 -0
  21. data/lib/bullet/detector/counter_cache.rb +14 -10
  22. data/lib/bullet/detector/n_plus_one_query.rb +21 -17
  23. data/lib/bullet/detector/unused_eager_loading.rb +30 -24
  24. data/lib/bullet/detector.rb +2 -0
  25. data/lib/bullet/ext/object.rb +6 -4
  26. data/lib/bullet/ext/string.rb +3 -1
  27. data/lib/bullet/mongoid4x.rb +7 -2
  28. data/lib/bullet/mongoid5x.rb +7 -2
  29. data/lib/bullet/mongoid6x.rb +11 -6
  30. data/lib/bullet/notification/base.rb +26 -19
  31. data/lib/bullet/notification/counter_cache.rb +3 -1
  32. data/lib/bullet/notification/n_plus_one_query.rb +7 -4
  33. data/lib/bullet/notification/unused_eager_loading.rb +7 -4
  34. data/lib/bullet/notification.rb +2 -0
  35. data/lib/bullet/notification_collector.rb +2 -1
  36. data/lib/bullet/rack.rb +22 -18
  37. data/lib/bullet/registry/association.rb +2 -0
  38. data/lib/bullet/registry/base.rb +2 -0
  39. data/lib/bullet/registry/object.rb +2 -0
  40. data/lib/bullet/registry.rb +2 -0
  41. data/lib/bullet/stack_trace_filter.rb +56 -18
  42. data/lib/bullet/version.rb +3 -2
  43. data/lib/bullet.rb +43 -33
  44. data/lib/generators/bullet/install_generator.rb +48 -0
  45. data/perf/benchmark.rb +12 -12
  46. data/rails/init.rb +2 -0
  47. data/spec/bullet/detector/association_spec.rb +6 -4
  48. data/spec/bullet/detector/base_spec.rb +2 -0
  49. data/spec/bullet/detector/counter_cache_spec.rb +13 -11
  50. data/spec/bullet/detector/n_plus_one_query_spec.rb +71 -46
  51. data/spec/bullet/detector/unused_eager_loading_spec.rb +21 -19
  52. data/spec/bullet/ext/object_spec.rb +10 -8
  53. data/spec/bullet/ext/string_spec.rb +7 -5
  54. data/spec/bullet/notification/base_spec.rb +39 -38
  55. data/spec/bullet/notification/counter_cache_spec.rb +5 -3
  56. data/spec/bullet/notification/n_plus_one_query_spec.rb +5 -3
  57. data/spec/bullet/notification/unused_eager_loading_spec.rb +4 -2
  58. data/spec/bullet/notification_collector_spec.rb +12 -10
  59. data/spec/bullet/rack_spec.rb +48 -47
  60. data/spec/bullet/registry/association_spec.rb +12 -10
  61. data/spec/bullet/registry/base_spec.rb +22 -20
  62. data/spec/bullet/registry/object_spec.rb +6 -4
  63. data/spec/bullet_spec.rb +24 -3
  64. data/spec/integration/{active_record4 → active_record}/association_spec.rb +176 -114
  65. data/spec/integration/counter_cache_spec.rb +15 -13
  66. data/spec/integration/mongoid/association_spec.rb +35 -33
  67. data/spec/models/address.rb +2 -0
  68. data/spec/models/author.rb +2 -0
  69. data/spec/models/base_user.rb +2 -0
  70. data/spec/models/category.rb +2 -0
  71. data/spec/models/city.rb +2 -0
  72. data/spec/models/client.rb +4 -0
  73. data/spec/models/comment.rb +3 -1
  74. data/spec/models/company.rb +2 -0
  75. data/spec/models/country.rb +2 -0
  76. data/spec/models/document.rb +4 -2
  77. data/spec/models/entry.rb +2 -0
  78. data/spec/models/firm.rb +3 -0
  79. data/spec/models/folder.rb +2 -0
  80. data/spec/models/group.rb +4 -0
  81. data/spec/models/mongoid/address.rb +3 -1
  82. data/spec/models/mongoid/category.rb +4 -2
  83. data/spec/models/mongoid/comment.rb +3 -1
  84. data/spec/models/mongoid/company.rb +3 -1
  85. data/spec/models/mongoid/entry.rb +3 -1
  86. data/spec/models/mongoid/post.rb +6 -4
  87. data/spec/models/mongoid/user.rb +2 -0
  88. data/spec/models/newspaper.rb +3 -1
  89. data/spec/models/page.rb +2 -0
  90. data/spec/models/person.rb +2 -0
  91. data/spec/models/pet.rb +2 -0
  92. data/spec/models/post.rb +17 -0
  93. data/spec/models/relationship.rb +2 -0
  94. data/spec/models/reply.rb +2 -0
  95. data/spec/models/student.rb +2 -0
  96. data/spec/models/submission.rb +2 -0
  97. data/spec/models/teacher.rb +2 -0
  98. data/spec/models/user.rb +2 -0
  99. data/spec/models/writer.rb +2 -0
  100. data/spec/spec_helper.rb +15 -13
  101. data/spec/support/bullet_ext.rb +2 -0
  102. data/spec/support/mongo_seed.rb +36 -42
  103. data/spec/support/rack_double.rb +13 -19
  104. data/spec/support/sqlite_seed.rb +84 -76
  105. data/tasks/bullet_tasks.rake +4 -2
  106. data/test.sh +2 -0
  107. data/update.sh +2 -0
  108. metadata +17 -11
  109. data/spec/integration/active_record5/association_spec.rb +0 -768
@@ -1,16 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module SaveWithBulletSupport
3
- def save(*args)
4
- was_new_record = new_record?
5
- super(*args).tap do |result|
6
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
7
- end
8
- end
9
-
10
- def save!(*args)
11
- was_new_record = new_record?
12
- super(*args).tap do |result|
13
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
5
+ def _create_record(*)
6
+ super do
7
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
8
+ yield(self) if block_given?
14
9
  end
15
10
  end
16
11
  end
@@ -18,38 +13,34 @@ module Bullet
18
13
  module ActiveRecord
19
14
  def self.enable
20
15
  require 'active_record'
21
- ::ActiveRecord::Base.class_eval do
22
- class <<self
23
- alias_method :origin_find_by_sql, :find_by_sql
24
- def find_by_sql(sql, binds = [], preparable: nil)
25
- result = origin_find_by_sql(sql, binds, preparable: nil)
26
- if Bullet.start?
27
- if result.is_a? Array
28
- if result.size > 1
29
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
30
- Bullet::Detector::CounterCache.add_possible_objects(result)
31
- elsif result.size == 1
32
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
33
- Bullet::Detector::CounterCache.add_impossible_object(result.first)
34
- end
35
- elsif result.is_a? ::ActiveRecord::Base
36
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
37
- Bullet::Detector::CounterCache.add_impossible_object(result)
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)
38
27
  end
28
+ elsif result.is_a? ::ActiveRecord::Base
29
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
30
+ Bullet::Detector::CounterCache.add_impossible_object(result)
39
31
  end
40
- result
41
32
  end
33
+ result
42
34
  end
43
- end
35
+ end)
44
36
 
45
37
  ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
46
38
 
47
- ::ActiveRecord::Relation.class_eval do
48
- alias_method :origin_records, :records
39
+ ::ActiveRecord::Relation.prepend(Module.new do
49
40
  # if select a collection of objects, then these objects have possible to cause N+1 query.
50
41
  # if select only one object, then the only one object has impossible to cause N+1 query.
51
42
  def records
52
- result = origin_records
43
+ result = super
53
44
  if Bullet.start?
54
45
  if result.first.class.name !~ /^HABTM_/
55
46
  if result.size > 1
@@ -63,11 +54,9 @@ module Bullet
63
54
  end
64
55
  result
65
56
  end
66
- end
67
-
68
- ::ActiveRecord::Associations::Preloader.class_eval do
69
- alias_method :origin_preloaders_for_one, :preloaders_for_one
57
+ end)
70
58
 
59
+ ::ActiveRecord::Associations::Preloader.prepend(Module.new do
71
60
  def preloaders_for_one(association, records, scope)
72
61
  if Bullet.start?
73
62
  records.compact!
@@ -78,16 +67,16 @@ module Bullet
78
67
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
79
68
  end
80
69
  end
81
- origin_preloaders_for_one(association, records, scope)
70
+ super
82
71
  end
83
- end
72
+ end)
84
73
 
85
- ::ActiveRecord::FinderMethods.class_eval do
74
+ ::ActiveRecord::FinderMethods.prepend(Module.new do
86
75
  # add includes in scope
87
- alias_method :origin_find_with_associations, :find_with_associations
88
76
  def find_with_associations
89
- return origin_find_with_associations { |r| yield r } if block_given?
90
- records = origin_find_with_associations
77
+ return super { |r| yield r } if block_given?
78
+
79
+ records = super
91
80
  if Bullet.start?
92
81
  associations = (eager_load_values + includes_values).uniq
93
82
  records.each do |record|
@@ -97,24 +86,37 @@ module Bullet
97
86
  end
98
87
  records
99
88
  end
100
- end
89
+ end)
101
90
 
102
- ::ActiveRecord::Associations::JoinDependency.class_eval do
103
- alias_method :origin_instantiate, :instantiate
104
- alias_method :origin_construct, :construct
105
- alias_method :origin_construct_model, :construct_model
91
+ ::ActiveRecord::Associations::JoinDependency.prepend(Module.new do
92
+ if ::ActiveRecord::Associations::JoinDependency.instance_method(:instantiate).parameters.last[0] == :block
93
+ # ActiveRecord >= 5.1.5
94
+ def instantiate(result_set, &block)
95
+ @bullet_eager_loadings = {}
96
+ records = super
106
97
 
107
- def instantiate(result_set, aliases)
108
- @bullet_eager_loadings = {}
109
- records = origin_instantiate(result_set, aliases)
98
+ if Bullet.start?
99
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
100
+ objects = eager_loadings_hash.keys
101
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
102
+ end
103
+ end
104
+ records
105
+ end
106
+ else
107
+ # ActiveRecord <= 5.1.4
108
+ def instantiate(result_set, aliases)
109
+ @bullet_eager_loadings = {}
110
+ records = super
110
111
 
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(objects, eager_loadings_hash[objects.first].to_a)
112
+ if Bullet.start?
113
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
114
+ objects = eager_loadings_hash.keys
115
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
116
+ end
115
117
  end
118
+ records
116
119
  end
117
- records
118
120
  end
119
121
 
120
122
  def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
@@ -123,24 +125,24 @@ module Bullet
123
125
  parent.children.each do |node|
124
126
  key = aliases.column_alias(node, node.primary_key)
125
127
  id = row[key]
126
- if id.nil?
127
- associations = node.reflection.name
128
- Bullet::Detector::Association.add_object_associations(ar_parent, associations)
129
- Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
130
- @bullet_eager_loadings[ar_parent.class] ||= {}
131
- @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
132
- @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
133
- end
128
+ next unless id.nil?
129
+
130
+ associations = node.reflection.name
131
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
132
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
133
+ @bullet_eager_loadings[ar_parent.class] ||= {}
134
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
135
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
134
136
  end
135
137
  end
136
138
  end
137
139
 
138
- origin_construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
140
+ super
139
141
  end
140
142
 
141
143
  # call join associations
142
144
  def construct_model(record, node, row, model_cache, id, aliases)
143
- result = origin_construct_model(record, node, row, model_cache, id, aliases)
145
+ result = super
144
146
 
145
147
  if Bullet.start?
146
148
  associations = node.reflection.name
@@ -153,21 +155,28 @@ module Bullet
153
155
 
154
156
  result
155
157
  end
156
- end
158
+ end)
157
159
 
158
- ::ActiveRecord::Associations::CollectionAssociation.class_eval do
159
- # call one to many associations
160
- alias_method :origin_load_target, :load_target
160
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new do
161
161
  def load_target
162
- records = origin_load_target
162
+ records = super
163
163
 
164
164
  if Bullet.start?
165
- if self.is_a? ::ActiveRecord::Associations::ThroughAssociation
166
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
167
- association = self.owner.association self.through_reflection.name
165
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
166
+ refl = reflection.through_reflection
167
+ Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
168
+ association = owner.association refl.name
168
169
  Array(association.target).each do |through_record|
169
170
  Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
170
171
  end
172
+
173
+ if refl.through_reflection?
174
+ while refl.through_reflection?
175
+ refl = refl.through_reflection
176
+ end
177
+
178
+ Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
179
+ end
171
180
  end
172
181
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
173
182
  if records.first.class.name !~ /^HABTM_/
@@ -183,28 +192,25 @@ module Bullet
183
192
  records
184
193
  end
185
194
 
186
- alias_method :origin_empty?, :empty?
187
195
  def empty?
188
196
  if Bullet.start? && !reflection.has_cached_counter?
189
197
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
190
198
  end
191
- origin_empty?
199
+ super
192
200
  end
193
201
 
194
- alias_method :origin_include?, :include?
195
202
  def include?(object)
196
203
  if Bullet.start?
197
204
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
198
205
  end
199
- origin_include?(object)
206
+ super
200
207
  end
201
- end
208
+ end)
202
209
 
203
- ::ActiveRecord::Associations::SingularAssociation.class_eval do
210
+ ::ActiveRecord::Associations::SingularAssociation.prepend(Module.new do
204
211
  # call has_one and belongs_to associations
205
- alias_method :origin_reader, :reader
206
- def reader(force_reload = false)
207
- result = origin_reader(force_reload)
212
+ def target
213
+ result = super()
208
214
  if Bullet.start?
209
215
  if owner.class.name !~ /^HABTM_/ && !@inversed
210
216
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
@@ -217,27 +223,32 @@ module Bullet
217
223
  end
218
224
  result
219
225
  end
220
- end
226
+ end)
221
227
 
222
- ::ActiveRecord::Associations::HasManyAssociation.class_eval do
223
- alias_method :origin_many_empty?, :empty?
228
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new do
224
229
  def empty?
225
- result = origin_many_empty?
230
+ result = super
226
231
  if Bullet.start? && !reflection.has_cached_counter?
227
232
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
228
233
  end
229
234
  result
230
235
  end
231
236
 
232
- alias_method :origin_count_records, :count_records
233
237
  def count_records
234
238
  result = reflection.has_cached_counter?
235
- if Bullet.start? && !result && !self.is_a?(::ActiveRecord::Associations::ThroughAssociation)
239
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
236
240
  Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
237
241
  end
238
- origin_count_records
242
+ super
239
243
  end
240
- end
244
+ end)
245
+
246
+ ::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
247
+ def writer(record)
248
+ Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
249
+ super
250
+ end
251
+ end)
241
252
  end
242
253
  end
243
254
  end
@@ -0,0 +1,232 @@
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(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)
27
+ 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
+ end
32
+ end
33
+ result
34
+ end
35
+ end)
36
+
37
+ ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
38
+
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)
52
+ end
53
+ end
54
+ end
55
+ result
56
+ 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)
66
+ end
67
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
68
+ end
69
+ end
70
+ super
71
+ 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)
84
+ end
85
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
86
+ 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
+
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)
100
+ end
101
+ end
102
+ records
103
+ end
104
+
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
120
+ end
121
+ end
122
+
123
+ super
124
+ end
125
+
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
137
+ end
138
+
139
+ result
140
+ 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)
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)
167
+ end
168
+ end
169
+ end
170
+ records
171
+ end
172
+
173
+ def empty?
174
+ if Bullet.start? && !reflection.has_cached_counter?
175
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
176
+ end
177
+ super
178
+ end
179
+
180
+ def include?(object)
181
+ if Bullet.start?
182
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
183
+ end
184
+ super
185
+ 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
199
+ end
200
+ end
201
+ end
202
+ result
203
+ end
204
+ end)
205
+
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)
211
+ end
212
+ result
213
+ end
214
+
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)
219
+ 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
+ end
229
+ end)
230
+ end
231
+ end
232
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Dependency
3
5
  def mongoid?
@@ -24,8 +26,10 @@ module Bullet
24
26
  'active_record5'
25
27
  elsif active_record51?
26
28
  'active_record5'
29
+ elsif active_record52?
30
+ 'active_record52'
27
31
  else
28
- raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
32
+ raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
29
33
  end
30
34
  end
31
35
  end
@@ -72,6 +76,10 @@ module Bullet
72
76
  active_record5? && ::ActiveRecord::VERSION::MINOR == 1
73
77
  end
74
78
 
79
+ def active_record52?
80
+ active_record5? && ::ActiveRecord::VERSION::MINOR == 2
81
+ end
82
+
75
83
  def mongoid4x?
76
84
  mongoid? && ::Mongoid::VERSION =~ /\A4/
77
85
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class Association < Base
@@ -7,7 +9,7 @@ module Bullet
7
9
  return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
8
10
  return unless object.primary_key_value
9
11
 
10
- Bullet.debug("Detector::Association#add_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
12
+ Bullet.debug('Detector::Association#add_object_associations', "object: #{object.bullet_key}, associations: #{associations}")
11
13
  object_associations.add(object.bullet_key, associations)
12
14
  end
13
15
 
@@ -16,7 +18,7 @@ module Bullet
16
18
  return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
17
19
  return unless object.primary_key_value
18
20
 
19
- Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
21
+ Bullet.debug('Detector::Association#add_call_object_associations', "object: #{object.bullet_key}, associations: #{associations}")
20
22
  call_object_associations.add(object.bullet_key, associations)
21
23
  end
22
24
 
@@ -37,36 +39,37 @@ module Bullet
37
39
  end
38
40
 
39
41
  private
42
+
40
43
  # object_associations keep the object relationships
41
44
  # that the object has many associations.
42
45
  # e.g. { "Post:1" => [:comments] }
43
46
  # the object_associations keep all associations that may be or may no be
44
47
  # unpreload associations or unused preload associations.
45
- def object_associations
46
- Thread.current[:bullet_object_associations]
47
- end
48
+ def object_associations
49
+ Thread.current[:bullet_object_associations]
50
+ end
48
51
 
49
52
  # call_object_associations keep the object relationships
50
53
  # that object.associations is called.
51
54
  # e.g. { "Post:1" => [:comments] }
52
55
  # they are used to detect unused preload associations.
53
- def call_object_associations
54
- Thread.current[:bullet_call_object_associations]
55
- end
56
+ def call_object_associations
57
+ Thread.current[:bullet_call_object_associations]
58
+ end
56
59
 
57
60
  # inversed_objects keeps object relationships
58
61
  # that association is inversed.
59
62
  # e.g. { "Comment:1" => ["post"] }
60
- def inversed_objects
61
- Thread.current[:bullet_inversed_objects]
62
- end
63
+ def inversed_objects
64
+ Thread.current[:bullet_inversed_objects]
65
+ end
63
66
 
64
67
  # eager_loadings keep the object relationships
65
68
  # that the associations are preloaded by find :include.
66
69
  # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
67
- def eager_loadings
68
- Thread.current[:bullet_eager_loadings]
69
- end
70
+ def eager_loadings
71
+ Thread.current[:bullet_eager_loadings]
72
+ end
70
73
  end
71
74
  end
72
75
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bullet
2
4
  module Detector
3
5
  class Base