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