bullet 5.6.1 → 5.7.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/CHANGELOG.md +3 -1
- data/Gemfile.rails-5.2 +15 -0
- data/Rakefile +0 -1
- data/lib/bullet.rb +13 -5
- data/lib/bullet/active_record52.rb +225 -0
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/detector/association.rb +1 -0
- data/lib/bullet/detector/counter_cache.rb +1 -0
- data/lib/bullet/detector/n_plus_one_query.rb +1 -0
- data/lib/bullet/detector/unused_eager_loading.rb +2 -1
- data/lib/bullet/mongoid4x.rb +1 -1
- data/lib/bullet/mongoid5x.rb +1 -1
- data/lib/bullet/mongoid6x.rb +1 -1
- data/lib/bullet/notification/base.rb +3 -2
- data/lib/bullet/notification/n_plus_one_query.rb +2 -1
- data/lib/bullet/notification/unused_eager_loading.rb +2 -1
- data/lib/bullet/rack.rb +1 -1
- data/lib/bullet/version.rb +1 -1
- data/perf/benchmark.rb +3 -5
- data/spec/bullet/detector/n_plus_one_query_spec.rb +2 -2
- data/spec/bullet/notification/base_spec.rb +0 -1
- data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -1
- data/spec/bullet/rack_spec.rb +6 -6
- data/spec/bullet_spec.rb +20 -1
- data/spec/integration/active_record/association_spec.rb +7 -7
- data/spec/spec_helper.rb +3 -3
- data/spec/support/mongo_seed.rb +2 -2
- data/spec/support/rack_double.rb +3 -2
- data/spec/support/sqlite_seed.rb +0 -1
- data/test.sh +2 -0
- data/update.sh +2 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c9650d2dbc870d2ac04be4ca0e19723ef6a85c2
|
4
|
+
data.tar.gz: 35839d605fb1e11687836dbcbe12572b1eb07775
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5064948ab9db44cc9b4082397990a3d91e93dc6986db2767a528a4a05734b1d1ba9231af337624d79d7e312d13e075afb53217191d017a3c511a0889c436d0a0
|
7
|
+
data.tar.gz: 2c12bd045e5e41b4db0761b07562a763d3be089231d5e420fc3d7d8d27a40f184216c9090667883a606e35fcdfd2eda3f276838e60696c642bff0939ab35e85f
|
data/CHANGELOG.md
CHANGED
data/Gemfile.rails-5.2
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'rails', '~> 5.2.0.beta1'
|
6
|
+
gem 'sqlite3'
|
7
|
+
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
|
8
|
+
gem 'activerecord-import'
|
9
|
+
|
10
|
+
gem "rspec"
|
11
|
+
|
12
|
+
platforms :rbx do
|
13
|
+
gem 'rubysl', '~> 2.0'
|
14
|
+
gem 'rubinius-developer_tools'
|
15
|
+
end
|
data/Rakefile
CHANGED
data/lib/bullet.rb
CHANGED
@@ -38,12 +38,12 @@ module Bullet
|
|
38
38
|
delegate *available_notifiers
|
39
39
|
|
40
40
|
def raise=(should_raise)
|
41
|
-
UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
|
41
|
+
UniformNotifier.raise = (should_raise ? Notification::UnoptimizedQueryError : false)
|
42
42
|
end
|
43
43
|
|
44
|
-
DETECTORS = [
|
44
|
+
DETECTORS = [Bullet::Detector::NPlusOneQuery,
|
45
45
|
Bullet::Detector::UnusedEagerLoading,
|
46
|
-
Bullet::Detector::CounterCache
|
46
|
+
Bullet::Detector::CounterCache].freeze
|
47
47
|
|
48
48
|
def enable=(enable)
|
49
49
|
@enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
|
@@ -87,12 +87,19 @@ module Bullet
|
|
87
87
|
@whitelist[options[:type]][options[:class_name]] << options[:association].to_sym
|
88
88
|
end
|
89
89
|
|
90
|
+
def delete_whitelist(options)
|
91
|
+
reset_whitelist
|
92
|
+
@whitelist[options[:type]][options[:class_name]] ||= []
|
93
|
+
@whitelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
|
94
|
+
@whitelist[options[:type]].delete_if { |key, val| val.empty? }
|
95
|
+
end
|
96
|
+
|
90
97
|
def get_whitelist_associations(type, class_name)
|
91
98
|
Array(@whitelist[type][class_name])
|
92
99
|
end
|
93
100
|
|
94
101
|
def reset_whitelist
|
95
|
-
@whitelist ||= {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
|
102
|
+
@whitelist ||= { :n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {} }
|
96
103
|
end
|
97
104
|
|
98
105
|
def clear_whitelist
|
@@ -163,7 +170,7 @@ module Bullet
|
|
163
170
|
for_each_active_notifier_with_notification do |notification|
|
164
171
|
responses << notification.notify_inline
|
165
172
|
end
|
166
|
-
responses.join(
|
173
|
+
responses.join("\n")
|
167
174
|
end
|
168
175
|
|
169
176
|
def perform_out_of_channel_notifications(env = {})
|
@@ -211,6 +218,7 @@ module Bullet
|
|
211
218
|
end
|
212
219
|
|
213
220
|
private
|
221
|
+
|
214
222
|
def for_each_active_notifier_with_notification
|
215
223
|
UniformNotifier.active_notifiers.each do |notifier|
|
216
224
|
notification_collector.collection.each do |notification|
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Bullet
|
2
|
+
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
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ActiveRecord
|
19
|
+
def self.enable
|
20
|
+
require 'active_record'
|
21
|
+
::ActiveRecord::Base.extend(Module.new {
|
22
|
+
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
23
|
+
result = super
|
24
|
+
if Bullet.start?
|
25
|
+
if result.is_a? Array
|
26
|
+
if result.size > 1
|
27
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
28
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
29
|
+
elsif result.size == 1
|
30
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
31
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
32
|
+
end
|
33
|
+
elsif result.is_a? ::ActiveRecord::Base
|
34
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
35
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
result
|
39
|
+
end
|
40
|
+
})
|
41
|
+
|
42
|
+
::ActiveRecord::Base.prepend(SaveWithBulletSupport)
|
43
|
+
|
44
|
+
::ActiveRecord::Relation.prepend(Module.new {
|
45
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
46
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
47
|
+
def records
|
48
|
+
result = super
|
49
|
+
if Bullet.start?
|
50
|
+
if result.first.class.name !~ /^HABTM_/
|
51
|
+
if result.size > 1
|
52
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
53
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
54
|
+
elsif result.size == 1
|
55
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
56
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
})
|
63
|
+
|
64
|
+
::ActiveRecord::Associations::Preloader.prepend(Module.new {
|
65
|
+
def preloaders_for_one(association, records, scope)
|
66
|
+
if Bullet.start?
|
67
|
+
records.compact!
|
68
|
+
if records.first.class.name !~ /^HABTM_/
|
69
|
+
records.each do |record|
|
70
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
71
|
+
end
|
72
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
super
|
76
|
+
end
|
77
|
+
})
|
78
|
+
|
79
|
+
::ActiveRecord::FinderMethods.prepend(Module.new {
|
80
|
+
# add includes in scope
|
81
|
+
def find_with_associations
|
82
|
+
return super { |r| yield r } if block_given?
|
83
|
+
records = super
|
84
|
+
if Bullet.start?
|
85
|
+
associations = (eager_load_values + includes_values).uniq
|
86
|
+
records.each do |record|
|
87
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
88
|
+
end
|
89
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
90
|
+
end
|
91
|
+
records
|
92
|
+
end
|
93
|
+
})
|
94
|
+
|
95
|
+
::ActiveRecord::Associations::JoinDependency.prepend(Module.new {
|
96
|
+
def instantiate(result_set, &block)
|
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)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
records
|
107
|
+
end
|
108
|
+
|
109
|
+
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
110
|
+
if Bullet.start?
|
111
|
+
unless ar_parent.nil?
|
112
|
+
parent.children.each do |node|
|
113
|
+
key = aliases.column_alias(node, node.primary_key)
|
114
|
+
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
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
super
|
128
|
+
end
|
129
|
+
|
130
|
+
# call join associations
|
131
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
132
|
+
result = super
|
133
|
+
|
134
|
+
if Bullet.start?
|
135
|
+
associations = node.reflection.name
|
136
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
137
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
138
|
+
@bullet_eager_loadings[record.class] ||= {}
|
139
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
140
|
+
@bullet_eager_loadings[record.class][record] << associations
|
141
|
+
end
|
142
|
+
|
143
|
+
result
|
144
|
+
end
|
145
|
+
})
|
146
|
+
|
147
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new {
|
148
|
+
def load_target
|
149
|
+
records = super
|
150
|
+
|
151
|
+
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
|
155
|
+
Array(association.target).each do |through_record|
|
156
|
+
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_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
|
+
})
|
187
|
+
|
188
|
+
::ActiveRecord::Associations::SingularAssociation.prepend(Module.new {
|
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
|
+
})
|
205
|
+
|
206
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new {
|
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 && !self.is_a?(::ActiveRecord::Associations::ThroughAssociation)
|
218
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
219
|
+
end
|
220
|
+
super
|
221
|
+
end
|
222
|
+
})
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
data/lib/bullet/dependency.rb
CHANGED
@@ -24,6 +24,8 @@ module Bullet
|
|
24
24
|
'active_record5'
|
25
25
|
elsif active_record51?
|
26
26
|
'active_record5'
|
27
|
+
elsif active_record52?
|
28
|
+
'active_record52'
|
27
29
|
else
|
28
30
|
raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
|
29
31
|
end
|
@@ -72,6 +74,10 @@ module Bullet
|
|
72
74
|
active_record5? && ::ActiveRecord::VERSION::MINOR == 1
|
73
75
|
end
|
74
76
|
|
77
|
+
def active_record52?
|
78
|
+
active_record5? && ::ActiveRecord::VERSION::MINOR == 2
|
79
|
+
end
|
80
|
+
|
75
81
|
def mongoid4x?
|
76
82
|
mongoid? && ::Mongoid::VERSION =~ /\A4/
|
77
83
|
end
|
@@ -40,7 +40,7 @@ module Bullet
|
|
40
40
|
if key_objects_overlap == k
|
41
41
|
to_add << [k, associations]
|
42
42
|
else
|
43
|
-
to_merge << [key_objects_overlap, (
|
43
|
+
to_merge << [key_objects_overlap, (eager_loadings[k].dup << associations)]
|
44
44
|
|
45
45
|
keys_without_objects = k - key_objects_overlap
|
46
46
|
to_merge << [keys_without_objects, eager_loadings[k]]
|
@@ -56,6 +56,7 @@ module Bullet
|
|
56
56
|
end
|
57
57
|
|
58
58
|
private
|
59
|
+
|
59
60
|
def create_notification(callers, klazz, associations)
|
60
61
|
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
|
61
62
|
|
data/lib/bullet/mongoid4x.rb
CHANGED
@@ -21,7 +21,7 @@ module Bullet
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def each(&block)
|
24
|
-
records = query.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
24
|
+
records = query.map { |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
25
25
|
if records.length > 1
|
26
26
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
27
27
|
elsif records.size == 1
|
data/lib/bullet/mongoid5x.rb
CHANGED
@@ -21,7 +21,7 @@ module Bullet
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def each(&block)
|
24
|
-
records = view.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
24
|
+
records = view.map { |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
25
25
|
if records.length > 1
|
26
26
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
27
27
|
elsif records.size == 1
|
data/lib/bullet/mongoid6x.rb
CHANGED
@@ -21,7 +21,7 @@ module Bullet
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def each(&block)
|
24
|
-
records = view.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
24
|
+
records = view.map { |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
25
25
|
if records.length > 1
|
26
26
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
27
27
|
elsif records.size == 1
|
@@ -6,7 +6,7 @@ module Bullet
|
|
6
6
|
|
7
7
|
def initialize(base_class, association_or_associations, path = nil)
|
8
8
|
@base_class = base_class
|
9
|
-
@associations = association_or_associations.is_a?(Array) ?
|
9
|
+
@associations = association_or_associations.is_a?(Array) ? association_or_associations : [association_or_associations]
|
10
10
|
@path = path
|
11
11
|
end
|
12
12
|
|
@@ -65,12 +65,13 @@ module Bullet
|
|
65
65
|
end
|
66
66
|
|
67
67
|
protected
|
68
|
+
|
68
69
|
def klazz_associations_str
|
69
70
|
" #{@base_class} => [#{@associations.map(&:inspect).join(', '.freeze)}]"
|
70
71
|
end
|
71
72
|
|
72
73
|
def associations_str
|
73
|
-
":includes => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
|
74
|
+
":includes => #{@associations.map { |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
|
74
75
|
end
|
75
76
|
end
|
76
77
|
end
|
data/lib/bullet/rack.rb
CHANGED
@@ -14,7 +14,7 @@ module Bullet
|
|
14
14
|
response_body = nil
|
15
15
|
if Bullet.notification?
|
16
16
|
if !file?(headers) && !sse?(headers) && !empty?(response) &&
|
17
|
-
|
17
|
+
status == 200 && !response_body(response).frozen? && html_request?(headers, response)
|
18
18
|
response_body = response_body(response)
|
19
19
|
append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
20
20
|
append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
data/lib/bullet/version.rb
CHANGED
data/perf/benchmark.rb
CHANGED
@@ -53,7 +53,7 @@ end
|
|
53
53
|
|
54
54
|
users_size = 100
|
55
55
|
posts_size = 1000
|
56
|
-
comments_size =
|
56
|
+
comments_size = 10_000
|
57
57
|
users = []
|
58
58
|
users_size.times do |i|
|
59
59
|
users << User.new(:name => "user#{i}")
|
@@ -63,20 +63,19 @@ users = User.all
|
|
63
63
|
|
64
64
|
posts = []
|
65
65
|
posts_size.times do |i|
|
66
|
-
posts << Post.new(:title => "Title #{i}", :body => "Body #{i}", :user => users[i%100])
|
66
|
+
posts << Post.new(:title => "Title #{i}", :body => "Body #{i}", :user => users[i % 100])
|
67
67
|
end
|
68
68
|
Post.import posts
|
69
69
|
posts = Post.all
|
70
70
|
|
71
71
|
comments = []
|
72
72
|
comments_size.times do |i|
|
73
|
-
comments << Comment.new(:body => "Comment #{i}", :post => posts[i%1000], :user => users[i%100])
|
73
|
+
comments << Comment.new(:body => "Comment #{i}", :post => posts[i % 1000], :user => users[i % 100])
|
74
74
|
end
|
75
75
|
Comment.import comments
|
76
76
|
|
77
77
|
puts 'Start benchmarking...'
|
78
78
|
|
79
|
-
|
80
79
|
Bullet.enable = true
|
81
80
|
|
82
81
|
Benchmark.bm(70) do |bm|
|
@@ -98,7 +97,6 @@ end
|
|
98
97
|
|
99
98
|
puts 'End benchmarking...'
|
100
99
|
|
101
|
-
|
102
100
|
# Run benchmark with bundler
|
103
101
|
#
|
104
102
|
# bundle exec ruby perf/benchmark.rb
|
@@ -87,7 +87,7 @@ module Bullet
|
|
87
87
|
end
|
88
88
|
|
89
89
|
context 'stacktrace_excludes' do
|
90
|
-
before { Bullet.stacktrace_excludes = [
|
90
|
+
before { Bullet.stacktrace_excludes = [/def/] }
|
91
91
|
after { Bullet.stacktrace_excludes = nil }
|
92
92
|
|
93
93
|
it 'should not create notification when stacktrace contains paths that are in the exclude list' do
|
@@ -114,7 +114,7 @@ module Bullet
|
|
114
114
|
end
|
115
115
|
|
116
116
|
context 'stacktrace_includes' do
|
117
|
-
before { Bullet.stacktrace_includes = [
|
117
|
+
before { Bullet.stacktrace_includes = ['def', /xyz/] }
|
118
118
|
after { Bullet.stacktrace_includes = nil }
|
119
119
|
|
120
120
|
it 'should include paths that are in the stacktrace_include list' do
|
@@ -6,7 +6,7 @@ module Bullet
|
|
6
6
|
subject { NPlusOneQuery.new([['caller1', 'caller2']], Post, [:comments, :votes], 'path') }
|
7
7
|
|
8
8
|
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n") }
|
9
|
-
it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([
|
9
|
+
it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nCall stack\n caller1\n caller2\n"]) }
|
10
10
|
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]") }
|
11
11
|
it { expect(subject.title).to eq('USE eager loading in path') }
|
12
12
|
end
|
data/spec/bullet/rack_spec.rb
CHANGED
@@ -8,13 +8,13 @@ module Bullet
|
|
8
8
|
|
9
9
|
context '#html_request?' do
|
10
10
|
it 'should be true if Content-Type is text/html and http body contains html tag' do
|
11
|
-
headers = {'Content-Type' => 'text/html'}
|
11
|
+
headers = { 'Content-Type' => 'text/html' }
|
12
12
|
response = double(:body => '<html><head></head><body></body></html>')
|
13
13
|
expect(middleware).to be_html_request(headers, response)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'should be true if Content-Type is text/html and http body contains html tag with attributes' do
|
17
|
-
headers = {'Content-Type' => 'text/html'}
|
17
|
+
headers = { 'Content-Type' => 'text/html' }
|
18
18
|
response = double(:body => "<html attr='hello'><head></head><body></body></html>")
|
19
19
|
expect(middleware).to be_html_request(headers, response)
|
20
20
|
end
|
@@ -26,13 +26,13 @@ module Bullet
|
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'should be false if Content-Type is javascript' do
|
29
|
-
headers = {'Content-Type' => 'text/javascript'}
|
29
|
+
headers = { 'Content-Type' => 'text/javascript' }
|
30
30
|
response = double(:body => '<html><head></head><body></body></html>')
|
31
31
|
expect(middleware).not_to be_html_request(headers, response)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should be false if response body doesn't contain html tag" do
|
35
|
-
headers = {'Content-Type' => 'text/html'}
|
35
|
+
headers = { 'Content-Type' => 'text/html' }
|
36
36
|
response = double(:body => '<div>Partial</div>')
|
37
37
|
expect(middleware).not_to be_html_request(headers, response)
|
38
38
|
end
|
@@ -68,7 +68,7 @@ module Bullet
|
|
68
68
|
expect(Bullet).to receive(:notification?).and_return(true)
|
69
69
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
70
70
|
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
71
|
-
status, headers, response = middleware.call({'Content-Type' => 'text/html'})
|
71
|
+
status, headers, response = middleware.call({ 'Content-Type' => 'text/html' })
|
72
72
|
expect(headers['Content-Length']).to eq('56')
|
73
73
|
expect(response).to eq(['<html><head></head><body><bullet></bullet></body></html>'])
|
74
74
|
end
|
@@ -79,7 +79,7 @@ module Bullet
|
|
79
79
|
app.response = response
|
80
80
|
expect(Bullet).to receive(:notification?).and_return(true)
|
81
81
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
82
|
-
status, headers, response = middleware.call({'Content-Type' => 'text/html'})
|
82
|
+
status, headers, response = middleware.call({ 'Content-Type' => 'text/html' })
|
83
83
|
expect(headers['Content-Length']).to eq('58')
|
84
84
|
end
|
85
85
|
end
|
data/spec/bullet_spec.rb
CHANGED
@@ -4,7 +4,6 @@ describe Bullet, focused: true do
|
|
4
4
|
subject { Bullet }
|
5
5
|
|
6
6
|
describe '#enable' do
|
7
|
-
|
8
7
|
context 'enable Bullet' do
|
9
8
|
before do
|
10
9
|
# Bullet.enable
|
@@ -95,6 +94,26 @@ describe Bullet, focused: true do
|
|
95
94
|
end
|
96
95
|
end
|
97
96
|
|
97
|
+
describe '#delete_whitelist' do
|
98
|
+
context "for 'special' class names" do
|
99
|
+
it 'is deleted from the whitelist successfully' do
|
100
|
+
Bullet.add_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
|
101
|
+
Bullet.delete_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
|
102
|
+
expect(Bullet.whitelist[:n_plus_one_query]).to eq({})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when exists multiple definitions' do
|
107
|
+
it 'is deleted from the whitelist successfully' do
|
108
|
+
Bullet.add_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :department)
|
109
|
+
Bullet.add_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :team)
|
110
|
+
Bullet.delete_whitelist(:type => :n_plus_one_query, :class_name => 'Klass', :association => :team)
|
111
|
+
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
112
|
+
expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
98
117
|
describe '#perform_out_of_channel_notifications' do
|
99
118
|
let(:notification) { double }
|
100
119
|
|
@@ -122,7 +122,7 @@ if active_record?
|
|
122
122
|
end
|
123
123
|
|
124
124
|
it 'should detect preload with category => posts => comments' do
|
125
|
-
Category.includes({:posts => :comments}).each do |category|
|
125
|
+
Category.includes({ :posts => :comments }).each do |category|
|
126
126
|
category.posts.each do |post|
|
127
127
|
post.comments.map(&:name)
|
128
128
|
end
|
@@ -134,7 +134,7 @@ if active_record?
|
|
134
134
|
end
|
135
135
|
|
136
136
|
it 'should detect preload with category => posts => comments with posts.id > 0' do
|
137
|
-
Category.includes({:posts => :comments}).where('posts.id > 0').references(:posts).each do |category|
|
137
|
+
Category.includes({ :posts => :comments }).where('posts.id > 0').references(:posts).each do |category|
|
138
138
|
category.posts.each do |post|
|
139
139
|
post.comments.map(&:name)
|
140
140
|
end
|
@@ -146,7 +146,7 @@ if active_record?
|
|
146
146
|
end
|
147
147
|
|
148
148
|
it 'should detect unused preload with category => posts => comments' do
|
149
|
-
Category.includes({:posts => :comments}).map(&:name)
|
149
|
+
Category.includes({ :posts => :comments }).map(&:name)
|
150
150
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
151
151
|
expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
|
152
152
|
|
@@ -154,7 +154,7 @@ if active_record?
|
|
154
154
|
end
|
155
155
|
|
156
156
|
it 'should detect unused preload with post => commnets, no category => posts' do
|
157
|
-
Category.includes({:posts => :comments}).each do |category|
|
157
|
+
Category.includes({ :posts => :comments }).each do |category|
|
158
158
|
category.posts.map(&:name)
|
159
159
|
end
|
160
160
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
@@ -257,7 +257,7 @@ if active_record?
|
|
257
257
|
|
258
258
|
context 'category => posts => writer' do
|
259
259
|
it 'should not detect unused preload associations' do
|
260
|
-
category = Category.includes({:posts => :writer}).order('id DESC').find_by_name('first')
|
260
|
+
category = Category.includes({ :posts => :writer }).order('id DESC').find_by_name('first')
|
261
261
|
category.posts.map do |post|
|
262
262
|
post.name
|
263
263
|
post.writer.name
|
@@ -409,7 +409,7 @@ if active_record?
|
|
409
409
|
end
|
410
410
|
|
411
411
|
it 'should detect unused preload with comment => author' do
|
412
|
-
Comment.includes([:author, {:post => :writer}]).where(['base_users.id = ?', BaseUser.first]).references(:base_users).each do |comment|
|
412
|
+
Comment.includes([:author, { :post => :writer }]).where(['base_users.id = ?', BaseUser.first]).references(:base_users).each do |comment|
|
413
413
|
comment.post.writer.name
|
414
414
|
end
|
415
415
|
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
@@ -430,7 +430,7 @@ if active_record?
|
|
430
430
|
|
431
431
|
it 'should not raise a stack error from posts to category' do
|
432
432
|
expect {
|
433
|
-
Comment.includes({:post => :category}).each do |com|
|
433
|
+
Comment.includes({ :post => :category }).each do |com|
|
434
434
|
com.post.category
|
435
435
|
end
|
436
436
|
}.not_to raise_error()
|
data/spec/spec_helper.rb
CHANGED
@@ -28,7 +28,7 @@ Bullet.enable = true
|
|
28
28
|
MODELS = File.join(File.dirname(__FILE__), 'models')
|
29
29
|
$LOAD_PATH.unshift(MODELS)
|
30
30
|
SUPPORT = File.join(File.dirname(__FILE__), 'support')
|
31
|
-
Dir[
|
31
|
+
Dir[File.join(SUPPORT, '*.rb')].reject { |filename| filename =~ /_seed.rb$/ }.sort.each { |file| require file }
|
32
32
|
|
33
33
|
RSpec.configure do |config|
|
34
34
|
config.extend Bullet::Dependency
|
@@ -41,7 +41,7 @@ if active_record?
|
|
41
41
|
ActiveRecord::Migration.verbose = false
|
42
42
|
|
43
43
|
# Autoload every active_record model for the test suite that sits in spec/models.
|
44
|
-
Dir[
|
44
|
+
Dir[File.join(MODELS, '*.rb')].sort.each do |filename|
|
45
45
|
name = File.basename(filename, '.rb')
|
46
46
|
autoload name.camelize.to_sym, name
|
47
47
|
end
|
@@ -71,7 +71,7 @@ end
|
|
71
71
|
|
72
72
|
if mongoid?
|
73
73
|
# Autoload every mongoid model for the test suite that sits in spec/models.
|
74
|
-
Dir[
|
74
|
+
Dir[File.join(MODELS, 'mongoid', '*.rb')].sort.each { |file| require file }
|
75
75
|
require File.join(SUPPORT, 'mongo_seed.rb')
|
76
76
|
|
77
77
|
RSpec.configure do |config|
|
data/spec/support/mongo_seed.rb
CHANGED
@@ -39,7 +39,7 @@ module Support
|
|
39
39
|
sessions: {
|
40
40
|
default: {
|
41
41
|
database: 'bullet',
|
42
|
-
hosts: [
|
42
|
+
hosts: ['localhost:27017']
|
43
43
|
}
|
44
44
|
}
|
45
45
|
)
|
@@ -50,7 +50,7 @@ module Support
|
|
50
50
|
clients: {
|
51
51
|
default: {
|
52
52
|
database: 'bullet',
|
53
|
-
hosts: [
|
53
|
+
hosts: ['localhost:27017']
|
54
54
|
}
|
55
55
|
}
|
56
56
|
)
|
data/spec/support/rack_double.rb
CHANGED
@@ -2,7 +2,7 @@ module Support
|
|
2
2
|
class AppDouble
|
3
3
|
def call env
|
4
4
|
env = @env
|
5
|
-
[
|
5
|
+
[status, headers, response]
|
6
6
|
end
|
7
7
|
|
8
8
|
def status= status
|
@@ -14,7 +14,7 @@ module Support
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def headers
|
17
|
-
@headers ||= {'Content-Type' => 'text/html'}
|
17
|
+
@headers ||= { 'Content-Type' => 'text/html' }
|
18
18
|
@headers
|
19
19
|
end
|
20
20
|
|
@@ -23,6 +23,7 @@ module Support
|
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
|
+
|
26
27
|
def status
|
27
28
|
@status || 200
|
28
29
|
end
|
data/spec/support/sqlite_seed.rb
CHANGED
data/test.sh
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
#bundle update rails && bundle exec rspec spec
|
2
2
|
#BUNDLE_GEMFILE=Gemfile.mongoid bundle update mongoid && BUNDLE_GEMFILE=Gemfile.mongoid bundle exec rspec spec
|
3
|
+
BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle exec rspec spec
|
4
|
+
BUNDLE_GEMFILE=Gemfile.rails-5.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.1 bundle exec rspec spec
|
3
5
|
BUNDLE_GEMFILE=Gemfile.rails-5.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.0 bundle exec rspec spec
|
4
6
|
BUNDLE_GEMFILE=Gemfile.rails-4.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.2 bundle exec rspec spec
|
5
7
|
BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle exec rspec spec
|
data/update.sh
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- Gemfile.rails-4.2
|
60
60
|
- Gemfile.rails-5.0
|
61
61
|
- Gemfile.rails-5.1
|
62
|
+
- Gemfile.rails-5.2
|
62
63
|
- Guardfile
|
63
64
|
- Hacking.md
|
64
65
|
- MIT-LICENSE
|
@@ -70,6 +71,7 @@ files:
|
|
70
71
|
- lib/bullet/active_record41.rb
|
71
72
|
- lib/bullet/active_record42.rb
|
72
73
|
- lib/bullet/active_record5.rb
|
74
|
+
- lib/bullet/active_record52.rb
|
73
75
|
- lib/bullet/dependency.rb
|
74
76
|
- lib/bullet/detector.rb
|
75
77
|
- lib/bullet/detector/association.rb
|
@@ -178,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
180
|
version: 1.3.6
|
179
181
|
requirements: []
|
180
182
|
rubyforge_project:
|
181
|
-
rubygems_version: 2.6.
|
183
|
+
rubygems_version: 2.6.13
|
182
184
|
signing_key:
|
183
185
|
specification_version: 4
|
184
186
|
summary: help to kill N+1 queries and unused eager loading.
|