bullet 4.14.5 → 4.14.10
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile.mongoid-5.0 +17 -0
- data/README.md +21 -0
- data/lib/bullet/active_record3.rb +65 -35
- data/lib/bullet/active_record3x.rb +54 -32
- data/lib/bullet/active_record4.rb +61 -31
- data/lib/bullet/active_record41.rb +62 -32
- data/lib/bullet/active_record42.rb +93 -49
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/detector/association.rb +16 -16
- data/lib/bullet/detector/counter_cache.rb +13 -13
- data/lib/bullet/detector/n_plus_one_query.rb +30 -30
- data/lib/bullet/detector/unused_eager_loading.rb +1 -1
- data/lib/bullet/ext/object.rb +3 -1
- data/lib/bullet/mongoid5x.rb +56 -0
- data/lib/bullet/rack.rb +2 -2
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +14 -4
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +27 -27
- data/spec/bullet/ext/object_spec.rb +6 -0
- data/spec/bullet/notification/base_spec.rb +2 -2
- data/spec/bullet_spec.rb +47 -0
- data/spec/integration/active_record3/association_spec.rb +11 -2
- data/spec/integration/active_record4/association_spec.rb +32 -2
- data/spec/integration/counter_cache_spec.rb +8 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/mongo_seed.rb +13 -0
- data/test.sh +1 -0
- data/update.sh +1 -0
- metadata +5 -3
|
@@ -8,29 +8,43 @@ module Bullet
|
|
|
8
8
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
9
9
|
def to_a
|
|
10
10
|
records = origin_to_a
|
|
11
|
-
if
|
|
12
|
-
if records.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
if Bullet.start?
|
|
12
|
+
if records.first.class.name !~ /^HABTM_/
|
|
13
|
+
if records.size > 1
|
|
14
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
15
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
16
|
+
elsif records.size == 1
|
|
17
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
18
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
19
|
+
end
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
records
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
::ActiveRecord::Persistence.class_eval do
|
|
27
|
+
def save_with_bullet(*args, &proc)
|
|
28
|
+
was_new_record = new_record?
|
|
29
|
+
save_without_bullet(*args, &proc).tap do |result|
|
|
30
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
alias_method_chain :save, :bullet
|
|
34
|
+
end
|
|
35
|
+
|
|
24
36
|
::ActiveRecord::Associations::Preloader.class_eval do
|
|
25
37
|
alias_method :origin_preloaders_on, :preloaders_on
|
|
26
38
|
|
|
27
39
|
def preloaders_on(association, records, scope)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
records.
|
|
31
|
-
|
|
40
|
+
if Bullet.start?
|
|
41
|
+
records.compact!
|
|
42
|
+
if records.first.class.name !~ /^HABTM_/
|
|
43
|
+
records.each do |record|
|
|
44
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
|
45
|
+
end
|
|
46
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
32
47
|
end
|
|
33
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
34
48
|
end
|
|
35
49
|
origin_preloaders_on(association, records, scope)
|
|
36
50
|
end
|
|
@@ -42,11 +56,13 @@ module Bullet
|
|
|
42
56
|
def find_with_associations
|
|
43
57
|
return origin_find_with_associations { |r| yield r } if block_given?
|
|
44
58
|
records = origin_find_with_associations
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
if Bullet.start?
|
|
60
|
+
associations = (eager_load_values + includes_values).uniq
|
|
61
|
+
records.each do |record|
|
|
62
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
63
|
+
end
|
|
64
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
48
65
|
end
|
|
49
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
50
66
|
records
|
|
51
67
|
end
|
|
52
68
|
end
|
|
@@ -59,9 +75,11 @@ module Bullet
|
|
|
59
75
|
@bullet_eager_loadings = {}
|
|
60
76
|
records = origin_instantiate(result_set, aliases)
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
if Bullet.start?
|
|
79
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
80
|
+
objects = eager_loadings_hash.keys
|
|
81
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
82
|
+
end
|
|
65
83
|
end
|
|
66
84
|
records
|
|
67
85
|
end
|
|
@@ -70,12 +88,14 @@ module Bullet
|
|
|
70
88
|
def construct_model(record, node, row, model_cache, id, aliases)
|
|
71
89
|
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
|
72
90
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
if Bullet.start?
|
|
92
|
+
associations = node.reflection.name
|
|
93
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
94
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
95
|
+
@bullet_eager_loadings[record.class] ||= {}
|
|
96
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
97
|
+
@bullet_eager_loadings[record.class][record] << associations
|
|
98
|
+
end
|
|
79
99
|
|
|
80
100
|
result
|
|
81
101
|
end
|
|
@@ -85,19 +105,25 @@ module Bullet
|
|
|
85
105
|
# call one to many associations
|
|
86
106
|
alias_method :origin_load_target, :load_target
|
|
87
107
|
def load_target
|
|
88
|
-
Bullet
|
|
108
|
+
if Bullet.start?
|
|
109
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
110
|
+
end
|
|
89
111
|
origin_load_target
|
|
90
112
|
end
|
|
91
113
|
|
|
92
114
|
alias_method :origin_empty?, :empty?
|
|
93
115
|
def empty?
|
|
94
|
-
Bullet
|
|
116
|
+
if Bullet.start?
|
|
117
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
118
|
+
end
|
|
95
119
|
origin_empty?
|
|
96
120
|
end
|
|
97
121
|
|
|
98
122
|
alias_method :origin_include?, :include?
|
|
99
123
|
def include?(object)
|
|
100
|
-
Bullet
|
|
124
|
+
if Bullet.start?
|
|
125
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
126
|
+
end
|
|
101
127
|
origin_include?(object)
|
|
102
128
|
end
|
|
103
129
|
end
|
|
@@ -107,9 +133,11 @@ module Bullet
|
|
|
107
133
|
alias_method :origin_reader, :reader
|
|
108
134
|
def reader(force_reload = false)
|
|
109
135
|
result = origin_reader(force_reload)
|
|
110
|
-
if
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
if Bullet.start?
|
|
137
|
+
if @owner.class.name !~ /^HABTM_/ && !@inversed
|
|
138
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
139
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
140
|
+
end
|
|
113
141
|
end
|
|
114
142
|
result
|
|
115
143
|
end
|
|
@@ -120,7 +148,9 @@ module Bullet
|
|
|
120
148
|
|
|
121
149
|
def has_cached_counter?(reflection = reflection())
|
|
122
150
|
result = origin_has_cached_counter?(reflection)
|
|
123
|
-
Bullet
|
|
151
|
+
if Bullet.start? && !result
|
|
152
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
153
|
+
end
|
|
124
154
|
result
|
|
125
155
|
end
|
|
126
156
|
end
|
|
@@ -7,31 +7,45 @@ module Bullet
|
|
|
7
7
|
alias_method :origin_find, :find
|
|
8
8
|
def find(*args)
|
|
9
9
|
result = origin_find(*args)
|
|
10
|
-
if
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
if Bullet.start?
|
|
11
|
+
if result.is_a? Array
|
|
12
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
13
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
|
14
|
+
elsif result.is_a? ::ActiveRecord::Base
|
|
15
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
16
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
17
|
+
end
|
|
16
18
|
end
|
|
17
19
|
result
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
24
|
+
::ActiveRecord::Persistence.class_eval do
|
|
25
|
+
def save_with_bullet(*args, &proc)
|
|
26
|
+
was_new_record = new_record?
|
|
27
|
+
save_without_bullet(*args, &proc).tap do |result|
|
|
28
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
alias_method_chain :save, :bullet
|
|
32
|
+
end
|
|
33
|
+
|
|
22
34
|
::ActiveRecord::Relation.class_eval do
|
|
23
35
|
alias_method :origin_to_a, :to_a
|
|
24
36
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
25
37
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
26
38
|
def to_a
|
|
27
39
|
records = origin_to_a
|
|
28
|
-
if
|
|
29
|
-
if records.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
if Bullet.start?
|
|
41
|
+
if records.first.class.name !~ /^HABTM_/
|
|
42
|
+
if records.size > 1
|
|
43
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
44
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
45
|
+
elsif records.size == 1
|
|
46
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
47
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
48
|
+
end
|
|
35
49
|
end
|
|
36
50
|
end
|
|
37
51
|
records
|
|
@@ -42,12 +56,14 @@ module Bullet
|
|
|
42
56
|
alias_method :origin_preloaders_on, :preloaders_on
|
|
43
57
|
|
|
44
58
|
def preloaders_on(association, records, scope)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
records.
|
|
48
|
-
|
|
59
|
+
if Bullet.start?
|
|
60
|
+
records.compact!
|
|
61
|
+
if records.first.class.name !~ /^HABTM_/
|
|
62
|
+
records.each do |record|
|
|
63
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
|
64
|
+
end
|
|
65
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
49
66
|
end
|
|
50
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
51
67
|
end
|
|
52
68
|
origin_preloaders_on(association, records, scope)
|
|
53
69
|
end
|
|
@@ -59,11 +75,13 @@ module Bullet
|
|
|
59
75
|
def find_with_associations
|
|
60
76
|
return origin_find_with_associations { |r| yield r } if block_given?
|
|
61
77
|
records = origin_find_with_associations
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
if Bullet.start?
|
|
79
|
+
associations = (eager_load_values + includes_values).uniq
|
|
80
|
+
records.each do |record|
|
|
81
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
82
|
+
end
|
|
83
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
65
84
|
end
|
|
66
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
67
85
|
records
|
|
68
86
|
end
|
|
69
87
|
end
|
|
@@ -76,9 +94,11 @@ module Bullet
|
|
|
76
94
|
@bullet_eager_loadings = {}
|
|
77
95
|
records = origin_instantiate(result_set, aliases)
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
if Bullet.start?
|
|
98
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
99
|
+
objects = eager_loadings_hash.keys
|
|
100
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
101
|
+
end
|
|
82
102
|
end
|
|
83
103
|
records
|
|
84
104
|
end
|
|
@@ -87,12 +107,14 @@ module Bullet
|
|
|
87
107
|
def construct_model(record, node, row, model_cache, id, aliases)
|
|
88
108
|
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
|
89
109
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
if Bullet.start?
|
|
111
|
+
associations = node.reflection.name
|
|
112
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
113
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
114
|
+
@bullet_eager_loadings[record.class] ||= {}
|
|
115
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
116
|
+
@bullet_eager_loadings[record.class][record] << associations
|
|
117
|
+
end
|
|
96
118
|
|
|
97
119
|
result
|
|
98
120
|
end
|
|
@@ -102,16 +124,18 @@ module Bullet
|
|
|
102
124
|
# call one to many associations
|
|
103
125
|
alias_method :origin_load_target, :load_target
|
|
104
126
|
def load_target
|
|
105
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
106
127
|
records = origin_load_target
|
|
107
128
|
|
|
108
|
-
if
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
if Bullet.start?
|
|
130
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
131
|
+
if records.first.class.name !~ /^HABTM_/
|
|
132
|
+
if records.size > 1
|
|
133
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
134
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
135
|
+
elsif records.size == 1
|
|
136
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
137
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
138
|
+
end
|
|
115
139
|
end
|
|
116
140
|
end
|
|
117
141
|
records
|
|
@@ -119,13 +143,17 @@ module Bullet
|
|
|
119
143
|
|
|
120
144
|
alias_method :origin_empty?, :empty?
|
|
121
145
|
def empty?
|
|
122
|
-
Bullet
|
|
146
|
+
if Bullet.start?
|
|
147
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
148
|
+
end
|
|
123
149
|
origin_empty?
|
|
124
150
|
end
|
|
125
151
|
|
|
126
152
|
alias_method :origin_include?, :include?
|
|
127
153
|
def include?(object)
|
|
128
|
-
Bullet
|
|
154
|
+
if Bullet.start?
|
|
155
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
156
|
+
end
|
|
129
157
|
origin_include?(object)
|
|
130
158
|
end
|
|
131
159
|
end
|
|
@@ -135,12 +163,15 @@ module Bullet
|
|
|
135
163
|
alias_method :origin_reader, :reader
|
|
136
164
|
def reader(force_reload = false)
|
|
137
165
|
result = origin_reader(force_reload)
|
|
138
|
-
if
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
166
|
+
if Bullet.start?
|
|
167
|
+
if @owner.class.name !~ /^HABTM_/ && !@inversed
|
|
168
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
169
|
+
if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
|
|
170
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
171
|
+
else
|
|
172
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
|
|
173
|
+
end
|
|
174
|
+
end
|
|
144
175
|
end
|
|
145
176
|
result
|
|
146
177
|
end
|
|
@@ -152,14 +183,27 @@ module Bullet
|
|
|
152
183
|
Thread.current[:bullet_collection_empty] = true
|
|
153
184
|
result = origin_many_empty?
|
|
154
185
|
Thread.current[:bullet_collection_empty] = nil
|
|
155
|
-
Bullet
|
|
186
|
+
if Bullet.start?
|
|
187
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
188
|
+
end
|
|
156
189
|
result
|
|
157
190
|
end
|
|
158
191
|
|
|
159
192
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
160
193
|
def has_cached_counter?(reflection = reflection())
|
|
161
194
|
result = origin_has_cached_counter?(reflection)
|
|
162
|
-
if !result && !Thread.current[:bullet_collection_empty]
|
|
195
|
+
if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
|
|
196
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
197
|
+
end
|
|
198
|
+
result
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
::ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
|
|
203
|
+
alias_method :origin_hmt_has_cached_counter?, :has_cached_counter?
|
|
204
|
+
def has_cached_counter?(reflection = reflection())
|
|
205
|
+
result = origin_hmt_has_cached_counter?(reflection)
|
|
206
|
+
if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
|
|
163
207
|
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
164
208
|
end
|
|
165
209
|
result
|
data/lib/bullet/dependency.rb
CHANGED
|
@@ -36,6 +36,8 @@ module Bullet
|
|
|
36
36
|
'mongoid3x'
|
|
37
37
|
elsif mongoid4x?
|
|
38
38
|
'mongoid4x'
|
|
39
|
+
elsif mongoid5x?
|
|
40
|
+
'mongoid5x'
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
end
|
|
@@ -83,5 +85,9 @@ module Bullet
|
|
|
83
85
|
def mongoid4x?
|
|
84
86
|
mongoid? && ::Mongoid::VERSION =~ /\A4/
|
|
85
87
|
end
|
|
88
|
+
|
|
89
|
+
def mongoid5x?
|
|
90
|
+
mongoid? && ::Mongoid::VERSION =~ /\A5/
|
|
91
|
+
end
|
|
86
92
|
end
|
|
87
93
|
end
|
|
@@ -20,6 +20,22 @@ module Bullet
|
|
|
20
20
|
call_object_associations.add(object.bullet_key, associations)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# possible_objects keep the class to object relationships
|
|
24
|
+
# that the objects may cause N+1 query.
|
|
25
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
26
|
+
def possible_objects
|
|
27
|
+
Thread.current[:bullet_possible_objects]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# impossible_objects keep the class to objects relationships
|
|
31
|
+
# that the objects may not cause N+1 query.
|
|
32
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
33
|
+
# if find collection returns only one object, then the object is impossible object,
|
|
34
|
+
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
|
35
|
+
def impossible_objects
|
|
36
|
+
Thread.current[:bullet_impossible_objects]
|
|
37
|
+
end
|
|
38
|
+
|
|
23
39
|
private
|
|
24
40
|
# object_associations keep the object relationships
|
|
25
41
|
# that the object has many associations.
|
|
@@ -38,22 +54,6 @@ module Bullet
|
|
|
38
54
|
Thread.current[:bullet_call_object_associations]
|
|
39
55
|
end
|
|
40
56
|
|
|
41
|
-
# possible_objects keep the class to object relationships
|
|
42
|
-
# that the objects may cause N+1 query.
|
|
43
|
-
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
44
|
-
def possible_objects
|
|
45
|
-
Thread.current[:bullet_possible_objects]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# impossible_objects keep the class to objects relationships
|
|
49
|
-
# that the objects may not cause N+1 query.
|
|
50
|
-
# e.g. { Post => ["Post:1", "Post:2"] }
|
|
51
|
-
# if find collection returns only one object, then the object is impossible object,
|
|
52
|
-
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
|
53
|
-
def impossible_objects
|
|
54
|
-
Thread.current[:bullet_impossible_objects]
|
|
55
|
-
end
|
|
56
|
-
|
|
57
57
|
# inversed_objects keeps object relationships
|
|
58
58
|
# that association is inversed.
|
|
59
59
|
# e.g. { "Comment:1" => ["post"] }
|
|
@@ -8,7 +8,7 @@ module Bullet
|
|
|
8
8
|
return unless object.primary_key_value
|
|
9
9
|
|
|
10
10
|
Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
|
|
11
|
-
if conditions_met?(object
|
|
11
|
+
if conditions_met?(object, associations)
|
|
12
12
|
create_notification object.class.to_s, associations
|
|
13
13
|
end
|
|
14
14
|
end
|
|
@@ -32,6 +32,18 @@ module Bullet
|
|
|
32
32
|
impossible_objects.add object.bullet_key
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def conditions_met?(object, associations)
|
|
36
|
+
possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def possible_objects
|
|
40
|
+
Thread.current[:bullet_counter_possible_objects]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def impossible_objects
|
|
44
|
+
Thread.current[:bullet_counter_impossible_objects]
|
|
45
|
+
end
|
|
46
|
+
|
|
35
47
|
private
|
|
36
48
|
def create_notification(klazz, associations)
|
|
37
49
|
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
|
|
@@ -41,18 +53,6 @@ module Bullet
|
|
|
41
53
|
Bullet.notification_collector.add notice
|
|
42
54
|
end
|
|
43
55
|
end
|
|
44
|
-
|
|
45
|
-
def possible_objects
|
|
46
|
-
Thread.current[:bullet_counter_possible_objects]
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def impossible_objects
|
|
50
|
-
Thread.current[:bullet_counter_impossible_objects]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def conditions_met?(bullet_key, associations)
|
|
54
|
-
possible_objects.include?(bullet_key) && !impossible_objects.include?(bullet_key)
|
|
55
|
-
end
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
end
|
|
@@ -15,7 +15,7 @@ module Bullet
|
|
|
15
15
|
add_call_object_associations(object, associations)
|
|
16
16
|
|
|
17
17
|
Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_key}, associations: #{associations}")
|
|
18
|
-
if conditions_met?(object
|
|
18
|
+
if conditions_met?(object, associations)
|
|
19
19
|
Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
|
|
20
20
|
create_notification caller_in_project, object.class.to_s, associations
|
|
21
21
|
end
|
|
@@ -49,6 +49,35 @@ module Bullet
|
|
|
49
49
|
inversed_objects.add object.bullet_key, association
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# decide whether the object.associations is unpreloaded or not.
|
|
53
|
+
def conditions_met?(object, associations)
|
|
54
|
+
possible?(object) && !impossible?(object) && !association?(object, associations)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def possible?(object)
|
|
58
|
+
possible_objects.include? object.bullet_key
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def impossible?(object)
|
|
62
|
+
impossible_objects.include? object.bullet_key
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# check if object => associations already exists in object_associations.
|
|
66
|
+
def association?(object, associations)
|
|
67
|
+
value = object_associations[object.bullet_key]
|
|
68
|
+
if value
|
|
69
|
+
value.each do |v|
|
|
70
|
+
# associations == v comparision order is important here because
|
|
71
|
+
# v variable might be a squeel node where :== method is redefined,
|
|
72
|
+
# so it does not compare values at all and return unexpected results
|
|
73
|
+
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
|
|
74
|
+
return true if result
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
52
81
|
private
|
|
53
82
|
def create_notification(callers, klazz, associations)
|
|
54
83
|
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
|
|
@@ -59,11 +88,6 @@ module Bullet
|
|
|
59
88
|
end
|
|
60
89
|
end
|
|
61
90
|
|
|
62
|
-
# decide whether the object.associations is unpreloaded or not.
|
|
63
|
-
def conditions_met?(bullet_key, associations)
|
|
64
|
-
possible?(bullet_key) && !impossible?(bullet_key) && !association?(bullet_key, associations)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
91
|
def caller_in_project
|
|
68
92
|
app_root = rails? ? Rails.root.to_s : Dir.pwd
|
|
69
93
|
vendor_root = app_root + "/vendor"
|
|
@@ -72,30 +96,6 @@ module Bullet
|
|
|
72
96
|
Bullet.stacktrace_includes.any? { |include| c.include?(include) }
|
|
73
97
|
end
|
|
74
98
|
end
|
|
75
|
-
|
|
76
|
-
def possible?(bullet_key)
|
|
77
|
-
possible_objects.include? bullet_key
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def impossible?(bullet_key)
|
|
81
|
-
impossible_objects.include? bullet_key
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# check if object => associations already exists in object_associations.
|
|
85
|
-
def association?(bullet_key, associations)
|
|
86
|
-
value = object_associations[bullet_key]
|
|
87
|
-
if value
|
|
88
|
-
value.each do |v|
|
|
89
|
-
# associations == v comparision order is important here because
|
|
90
|
-
# v variable might be a squeel node where :== method is redefined,
|
|
91
|
-
# so it does not compare values at all and return unexpected results
|
|
92
|
-
result = v.is_a?(Hash) ? v.has_key?(associations) : associations == v
|
|
93
|
-
return true if result
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
return false
|
|
98
|
-
end
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
end
|
|
@@ -48,7 +48,7 @@ module Bullet
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
eager_loadings.add(*to_add) if to_add
|
|
51
|
-
to_merge.each { |k,val| eager_loadings.merge k, val }
|
|
51
|
+
to_merge.each { |k, val| eager_loadings.merge k, val }
|
|
52
52
|
to_delete.each { |k| eager_loadings.delete k }
|
|
53
53
|
|
|
54
54
|
eager_loadings.add bullet_keys, associations unless bullet_keys.empty?
|
data/lib/bullet/ext/object.rb
CHANGED
|
@@ -4,7 +4,9 @@ class Object
|
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
def primary_key_value
|
|
7
|
-
if self.class.respond_to?(:
|
|
7
|
+
if self.class.respond_to?(:primary_keys) && self.class.primary_keys
|
|
8
|
+
self.class.primary_keys.map { |primary_key| self.send primary_key }.join(',')
|
|
9
|
+
elsif self.class.respond_to?(:primary_key) && self.class.primary_key
|
|
8
10
|
self.send self.class.primary_key
|
|
9
11
|
else
|
|
10
12
|
self.id
|