bullet 4.14.0 → 5.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.
- checksums.yaml +4 -4
- data/.travis.yml +56 -1
- data/CHANGELOG.md +53 -0
- data/Gemfile.mongoid +0 -2
- data/Gemfile.mongoid-2.4 +2 -4
- data/Gemfile.mongoid-2.5 +2 -4
- data/Gemfile.mongoid-2.6 +1 -3
- data/Gemfile.mongoid-2.7 +2 -4
- data/Gemfile.mongoid-2.8 +2 -4
- data/Gemfile.mongoid-3.0 +2 -4
- data/Gemfile.mongoid-3.1 +2 -4
- data/Gemfile.mongoid-4.0 +1 -3
- data/Gemfile.mongoid-5.0 +17 -0
- data/Gemfile.rails-3.0 +1 -3
- data/Gemfile.rails-3.1 +1 -3
- data/Gemfile.rails-3.2 +1 -3
- data/Gemfile.rails-4.0 +1 -3
- data/Gemfile.rails-4.1 +1 -3
- data/Gemfile.rails-4.2 +1 -3
- data/Gemfile.rails-5.0 +17 -0
- data/README.md +31 -2
- data/bullet.gemspec +1 -1
- data/lib/bullet/active_record3.rb +65 -35
- data/lib/bullet/active_record3x.rb +54 -32
- data/lib/bullet/active_record4.rb +62 -30
- data/lib/bullet/active_record41.rb +63 -32
- data/lib/bullet/active_record42.rb +105 -40
- data/lib/bullet/active_record5.rb +217 -0
- data/lib/bullet/dependency.rb +16 -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 +33 -24
- 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/notification/base.rb +1 -1
- data/lib/bullet/notification/n_plus_one_query.rb +6 -0
- data/lib/bullet/rack.rb +19 -11
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +28 -11
- data/spec/bullet/detector/counter_cache_spec.rb +8 -8
- data/spec/bullet/detector/n_plus_one_query_spec.rb +42 -27
- data/spec/bullet/ext/object_spec.rb +6 -0
- data/spec/bullet/notification/base_spec.rb +2 -2
- data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -3
- data/spec/bullet/notification/unused_eager_loading_spec.rb +1 -1
- data/spec/bullet/rack_spec.rb +3 -3
- 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 +58 -3
- data/spec/integration/active_record5/association_spec.rb +715 -0
- data/spec/integration/counter_cache_spec.rb +17 -1
- data/spec/models/category.rb +4 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +7 -2
- data/spec/models/reply.rb +3 -0
- data/spec/models/submission.rb +1 -1
- data/spec/spec_helper.rb +3 -2
- data/spec/support/mongo_seed.rb +13 -0
- data/spec/support/sqlite_seed.rb +14 -6
- data/test.sh +1 -0
- data/update.sh +15 -0
- metadata +16 -7
|
@@ -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
|
|
@@ -40,12 +54,15 @@ module Bullet
|
|
|
40
54
|
# add includes in scope
|
|
41
55
|
alias_method :origin_find_with_associations, :find_with_associations
|
|
42
56
|
def find_with_associations
|
|
57
|
+
return origin_find_with_associations { |r| yield r } if block_given?
|
|
43
58
|
records = origin_find_with_associations
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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)
|
|
47
65
|
end
|
|
48
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
49
66
|
records
|
|
50
67
|
end
|
|
51
68
|
end
|
|
@@ -58,9 +75,11 @@ module Bullet
|
|
|
58
75
|
@bullet_eager_loadings = {}
|
|
59
76
|
records = origin_instantiate(result_set, aliases)
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
64
83
|
end
|
|
65
84
|
records
|
|
66
85
|
end
|
|
@@ -69,12 +88,14 @@ module Bullet
|
|
|
69
88
|
def construct_model(record, node, row, model_cache, id, aliases)
|
|
70
89
|
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
78
99
|
|
|
79
100
|
result
|
|
80
101
|
end
|
|
@@ -84,19 +105,25 @@ module Bullet
|
|
|
84
105
|
# call one to many associations
|
|
85
106
|
alias_method :origin_load_target, :load_target
|
|
86
107
|
def load_target
|
|
87
|
-
Bullet
|
|
108
|
+
if Bullet.start?
|
|
109
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
110
|
+
end
|
|
88
111
|
origin_load_target
|
|
89
112
|
end
|
|
90
113
|
|
|
91
114
|
alias_method :origin_empty?, :empty?
|
|
92
115
|
def empty?
|
|
93
|
-
Bullet
|
|
116
|
+
if Bullet.start?
|
|
117
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
118
|
+
end
|
|
94
119
|
origin_empty?
|
|
95
120
|
end
|
|
96
121
|
|
|
97
122
|
alias_method :origin_include?, :include?
|
|
98
123
|
def include?(object)
|
|
99
|
-
Bullet
|
|
124
|
+
if Bullet.start?
|
|
125
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
126
|
+
end
|
|
100
127
|
origin_include?(object)
|
|
101
128
|
end
|
|
102
129
|
end
|
|
@@ -106,9 +133,11 @@ module Bullet
|
|
|
106
133
|
alias_method :origin_reader, :reader
|
|
107
134
|
def reader(force_reload = false)
|
|
108
135
|
result = origin_reader(force_reload)
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
112
141
|
end
|
|
113
142
|
result
|
|
114
143
|
end
|
|
@@ -119,7 +148,9 @@ module Bullet
|
|
|
119
148
|
|
|
120
149
|
def has_cached_counter?(reflection = reflection())
|
|
121
150
|
result = origin_has_cached_counter?(reflection)
|
|
122
|
-
Bullet
|
|
151
|
+
if Bullet.start? && !result
|
|
152
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
153
|
+
end
|
|
123
154
|
result
|
|
124
155
|
end
|
|
125
156
|
end
|
|
@@ -2,19 +2,50 @@ module Bullet
|
|
|
2
2
|
module ActiveRecord
|
|
3
3
|
def self.enable
|
|
4
4
|
require 'active_record'
|
|
5
|
+
::ActiveRecord::Base.class_eval do
|
|
6
|
+
class <<self
|
|
7
|
+
alias_method :origin_find, :find
|
|
8
|
+
def find(*args)
|
|
9
|
+
result = origin_find(*args)
|
|
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
|
|
18
|
+
end
|
|
19
|
+
result
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
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
|
+
|
|
5
34
|
::ActiveRecord::Relation.class_eval do
|
|
6
35
|
alias_method :origin_to_a, :to_a
|
|
7
36
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
8
37
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
9
38
|
def to_a
|
|
10
39
|
records = origin_to_a
|
|
11
|
-
if
|
|
12
|
-
if records.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
18
49
|
end
|
|
19
50
|
end
|
|
20
51
|
records
|
|
@@ -25,12 +56,14 @@ module Bullet
|
|
|
25
56
|
alias_method :origin_preloaders_on, :preloaders_on
|
|
26
57
|
|
|
27
58
|
def preloaders_on(association, records, scope)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
records.
|
|
31
|
-
|
|
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)
|
|
32
66
|
end
|
|
33
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
34
67
|
end
|
|
35
68
|
origin_preloaders_on(association, records, scope)
|
|
36
69
|
end
|
|
@@ -40,12 +73,15 @@ module Bullet
|
|
|
40
73
|
# add includes in scope
|
|
41
74
|
alias_method :origin_find_with_associations, :find_with_associations
|
|
42
75
|
def find_with_associations
|
|
76
|
+
return origin_find_with_associations { |r| yield r } if block_given?
|
|
43
77
|
records = origin_find_with_associations
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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)
|
|
47
84
|
end
|
|
48
|
-
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
49
85
|
records
|
|
50
86
|
end
|
|
51
87
|
end
|
|
@@ -58,9 +94,11 @@ module Bullet
|
|
|
58
94
|
@bullet_eager_loadings = {}
|
|
59
95
|
records = origin_instantiate(result_set, aliases)
|
|
60
96
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
64
102
|
end
|
|
65
103
|
records
|
|
66
104
|
end
|
|
@@ -69,12 +107,14 @@ module Bullet
|
|
|
69
107
|
def construct_model(record, node, row, model_cache, id, aliases)
|
|
70
108
|
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
|
71
109
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
78
118
|
|
|
79
119
|
result
|
|
80
120
|
end
|
|
@@ -84,16 +124,18 @@ module Bullet
|
|
|
84
124
|
# call one to many associations
|
|
85
125
|
alias_method :origin_load_target, :load_target
|
|
86
126
|
def load_target
|
|
87
|
-
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
88
127
|
records = origin_load_target
|
|
89
128
|
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
97
139
|
end
|
|
98
140
|
end
|
|
99
141
|
records
|
|
@@ -101,13 +143,17 @@ module Bullet
|
|
|
101
143
|
|
|
102
144
|
alias_method :origin_empty?, :empty?
|
|
103
145
|
def empty?
|
|
104
|
-
Bullet
|
|
146
|
+
if Bullet.start?
|
|
147
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
148
|
+
end
|
|
105
149
|
origin_empty?
|
|
106
150
|
end
|
|
107
151
|
|
|
108
152
|
alias_method :origin_include?, :include?
|
|
109
153
|
def include?(object)
|
|
110
|
-
Bullet
|
|
154
|
+
if Bullet.start?
|
|
155
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
156
|
+
end
|
|
111
157
|
origin_include?(object)
|
|
112
158
|
end
|
|
113
159
|
end
|
|
@@ -117,9 +163,15 @@ module Bullet
|
|
|
117
163
|
alias_method :origin_reader, :reader
|
|
118
164
|
def reader(force_reload = false)
|
|
119
165
|
result = origin_reader(force_reload)
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
123
175
|
end
|
|
124
176
|
result
|
|
125
177
|
end
|
|
@@ -131,14 +183,27 @@ module Bullet
|
|
|
131
183
|
Thread.current[:bullet_collection_empty] = true
|
|
132
184
|
result = origin_many_empty?
|
|
133
185
|
Thread.current[:bullet_collection_empty] = nil
|
|
134
|
-
Bullet
|
|
186
|
+
if Bullet.start?
|
|
187
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
188
|
+
end
|
|
135
189
|
result
|
|
136
190
|
end
|
|
137
191
|
|
|
138
192
|
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
139
193
|
def has_cached_counter?(reflection = reflection())
|
|
140
194
|
result = origin_has_cached_counter?(reflection)
|
|
141
|
-
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]
|
|
142
207
|
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
143
208
|
end
|
|
144
209
|
result
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
module Bullet
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
def self.enable
|
|
4
|
+
require 'active_record'
|
|
5
|
+
::ActiveRecord::Base.class_eval do
|
|
6
|
+
class <<self
|
|
7
|
+
alias_method :origin_find, :find
|
|
8
|
+
def find(*args)
|
|
9
|
+
result = origin_find(*args)
|
|
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
|
|
18
|
+
end
|
|
19
|
+
result
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
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
|
+
|
|
34
|
+
::ActiveRecord::Relation.class_eval do
|
|
35
|
+
alias_method :origin_to_a, :to_a
|
|
36
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
37
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
38
|
+
def to_a
|
|
39
|
+
records = origin_to_a
|
|
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
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
records
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
::ActiveRecord::Associations::Association.class_eval do
|
|
56
|
+
alias_method :origin_initialize, :initialize
|
|
57
|
+
def initialize(owner, reflection)
|
|
58
|
+
origin_initialize(owner, reflection)
|
|
59
|
+
reflection.set_owner owner
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
::ActiveRecord::Associations::Preloader.class_eval do
|
|
64
|
+
alias_method :origin_preloaders_on, :preloaders_on
|
|
65
|
+
|
|
66
|
+
def preloaders_on(association, records, scope)
|
|
67
|
+
if Bullet.start?
|
|
68
|
+
records.compact!
|
|
69
|
+
if records.first.class.name !~ /^HABTM_/
|
|
70
|
+
records.each do |record|
|
|
71
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
|
72
|
+
end
|
|
73
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
origin_preloaders_on(association, records, scope)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
::ActiveRecord::FinderMethods.class_eval do
|
|
81
|
+
# add includes in scope
|
|
82
|
+
alias_method :origin_find_with_associations, :find_with_associations
|
|
83
|
+
def find_with_associations
|
|
84
|
+
return origin_find_with_associations { |r| yield r } if block_given?
|
|
85
|
+
records = origin_find_with_associations
|
|
86
|
+
if Bullet.start?
|
|
87
|
+
associations = (eager_load_values + includes_values).uniq
|
|
88
|
+
records.each do |record|
|
|
89
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
90
|
+
end
|
|
91
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
92
|
+
end
|
|
93
|
+
records
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
|
98
|
+
alias_method :origin_instantiate, :instantiate
|
|
99
|
+
alias_method :origin_construct_model, :construct_model
|
|
100
|
+
|
|
101
|
+
def instantiate(result_set, aliases)
|
|
102
|
+
@bullet_eager_loadings = {}
|
|
103
|
+
records = origin_instantiate(result_set, aliases)
|
|
104
|
+
|
|
105
|
+
if Bullet.start?
|
|
106
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
|
107
|
+
objects = eager_loadings_hash.keys
|
|
108
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
records
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# call join associations
|
|
115
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
|
116
|
+
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
|
117
|
+
|
|
118
|
+
if Bullet.start?
|
|
119
|
+
associations = node.reflection.name
|
|
120
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
121
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
122
|
+
@bullet_eager_loadings[record.class] ||= {}
|
|
123
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
124
|
+
@bullet_eager_loadings[record.class][record] << associations
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
result
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
|
132
|
+
# call one to many associations
|
|
133
|
+
alias_method :origin_load_target, :load_target
|
|
134
|
+
def load_target
|
|
135
|
+
records = origin_load_target
|
|
136
|
+
|
|
137
|
+
if Bullet.start?
|
|
138
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
|
139
|
+
if records.first.class.name !~ /^HABTM_/
|
|
140
|
+
if records.size > 1
|
|
141
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
142
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
143
|
+
elsif records.size == 1
|
|
144
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
145
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
records
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
alias_method :origin_empty?, :empty?
|
|
153
|
+
def empty?
|
|
154
|
+
if Bullet.start?
|
|
155
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
156
|
+
end
|
|
157
|
+
origin_empty?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
alias_method :origin_include?, :include?
|
|
161
|
+
def include?(object)
|
|
162
|
+
if Bullet.start?
|
|
163
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
164
|
+
end
|
|
165
|
+
origin_include?(object)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
|
170
|
+
# call has_one and belongs_to associations
|
|
171
|
+
alias_method :origin_reader, :reader
|
|
172
|
+
def reader(force_reload = false)
|
|
173
|
+
result = origin_reader(force_reload)
|
|
174
|
+
if Bullet.start?
|
|
175
|
+
if @owner.class.name !~ /^HABTM_/ && !@inversed
|
|
176
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
177
|
+
if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
|
|
178
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
179
|
+
else
|
|
180
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
result
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
|
189
|
+
alias_method :origin_many_empty?, :empty?
|
|
190
|
+
def empty?
|
|
191
|
+
Thread.current[:bullet_collection_empty] = true
|
|
192
|
+
result = origin_many_empty?
|
|
193
|
+
Thread.current[:bullet_collection_empty] = nil
|
|
194
|
+
if Bullet.start?
|
|
195
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
|
196
|
+
end
|
|
197
|
+
result
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
::ActiveRecord::Reflection::AbstractReflection.class_eval do
|
|
202
|
+
def set_owner(owner)
|
|
203
|
+
@owner = owner
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
|
207
|
+
def has_cached_counter?
|
|
208
|
+
result = origin_has_cached_counter?
|
|
209
|
+
if Bullet.start? && !result && !Thread.current[:bullet_collection_empty]
|
|
210
|
+
Bullet::Detector::CounterCache.add_counter_cache(@owner, @name)
|
|
211
|
+
end
|
|
212
|
+
result
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
data/lib/bullet/dependency.rb
CHANGED
|
@@ -24,6 +24,8 @@ module Bullet
|
|
|
24
24
|
'active_record41'
|
|
25
25
|
elsif active_record42?
|
|
26
26
|
'active_record42'
|
|
27
|
+
elsif active_record50?
|
|
28
|
+
'active_record5'
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
end
|
|
@@ -36,6 +38,8 @@ module Bullet
|
|
|
36
38
|
'mongoid3x'
|
|
37
39
|
elsif mongoid4x?
|
|
38
40
|
'mongoid4x'
|
|
41
|
+
elsif mongoid5x?
|
|
42
|
+
'mongoid5x'
|
|
39
43
|
end
|
|
40
44
|
end
|
|
41
45
|
end
|
|
@@ -48,6 +52,10 @@ module Bullet
|
|
|
48
52
|
active_record? && ::ActiveRecord::VERSION::MAJOR == 4
|
|
49
53
|
end
|
|
50
54
|
|
|
55
|
+
def active_record5?
|
|
56
|
+
active_record? && ::ActiveRecord::VERSION::MAJOR == 5
|
|
57
|
+
end
|
|
58
|
+
|
|
51
59
|
def active_record30?
|
|
52
60
|
active_record3? && ::ActiveRecord::VERSION::MINOR == 0
|
|
53
61
|
end
|
|
@@ -72,6 +80,10 @@ module Bullet
|
|
|
72
80
|
active_record4? && ::ActiveRecord::VERSION::MINOR == 2
|
|
73
81
|
end
|
|
74
82
|
|
|
83
|
+
def active_record50?
|
|
84
|
+
active_record5? && ::ActiveRecord::VERSION::MINOR == 0
|
|
85
|
+
end
|
|
86
|
+
|
|
75
87
|
def mongoid2x?
|
|
76
88
|
mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
|
|
77
89
|
end
|
|
@@ -83,5 +95,9 @@ module Bullet
|
|
|
83
95
|
def mongoid4x?
|
|
84
96
|
mongoid? && ::Mongoid::VERSION =~ /\A4/
|
|
85
97
|
end
|
|
98
|
+
|
|
99
|
+
def mongoid5x?
|
|
100
|
+
mongoid? && ::Mongoid::VERSION =~ /\A5/
|
|
101
|
+
end
|
|
86
102
|
end
|
|
87
103
|
end
|