bullet 4.14.3 → 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 +42 -2
- data/Gemfile.mongoid +0 -2
- data/Gemfile.mongoid-2.4 +0 -2
- data/Gemfile.mongoid-2.5 +0 -2
- data/Gemfile.mongoid-2.6 +0 -2
- data/Gemfile.mongoid-2.7 +0 -2
- data/Gemfile.mongoid-2.8 +0 -2
- data/Gemfile.mongoid-3.0 +0 -2
- data/Gemfile.mongoid-3.1 +0 -2
- data/Gemfile.mongoid-4.0 +0 -2
- data/Gemfile.mongoid-5.0 +17 -0
- data/Gemfile.rails-3.1 +0 -2
- data/Gemfile.rails-3.2 +0 -2
- data/Gemfile.rails-4.0 +0 -2
- data/Gemfile.rails-4.1 +0 -2
- data/Gemfile.rails-4.2 +0 -2
- data/Gemfile.rails-5.0 +17 -0
- data/README.md +29 -1
- 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 +61 -31
- data/lib/bullet/active_record41.rb +63 -32
- data/lib/bullet/active_record42.rb +105 -43
- 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/n_plus_one_query.rb +6 -0
- data/lib/bullet/rack.rb +4 -6
- 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/rack_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/active_record5/association_spec.rb +715 -0
- data/spec/integration/counter_cache_spec.rb +17 -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 +13 -7
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
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Bullet
|
|
2
|
+
module Mongoid
|
|
3
|
+
def self.enable
|
|
4
|
+
require 'mongoid'
|
|
5
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
|
6
|
+
alias_method :origin_first, :first
|
|
7
|
+
alias_method :origin_last, :last
|
|
8
|
+
alias_method :origin_each, :each
|
|
9
|
+
alias_method :origin_eager_load, :eager_load
|
|
10
|
+
|
|
11
|
+
def first
|
|
12
|
+
result = origin_first
|
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def last
|
|
18
|
+
result = origin_last
|
|
19
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
20
|
+
result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def each(&block)
|
|
24
|
+
records = view.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
|
25
|
+
if records.length > 1
|
|
26
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
|
+
elsif records.size == 1
|
|
28
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
|
+
end
|
|
30
|
+
origin_each(&block)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def eager_load(docs)
|
|
34
|
+
associations = criteria.inclusions.map(&:name)
|
|
35
|
+
docs.each do |doc|
|
|
36
|
+
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
|
37
|
+
end
|
|
38
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
|
39
|
+
origin_eager_load(docs)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
::Mongoid::Relations::Accessors.class_eval do
|
|
44
|
+
alias_method :origin_get_relation, :get_relation
|
|
45
|
+
|
|
46
|
+
def get_relation(name, metadata, object, reload = false)
|
|
47
|
+
result = origin_get_relation(name, metadata, object, reload)
|
|
48
|
+
if metadata.macro !~ /embed/
|
|
49
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
|
50
|
+
end
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -15,6 +15,12 @@ module Bullet
|
|
|
15
15
|
"N+1 Query #{@path ? "in #{@path}" : 'detected'}"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def notification_data
|
|
19
|
+
super.merge(
|
|
20
|
+
:backtrace => @callers
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
protected
|
|
19
25
|
def call_stack_messages
|
|
20
26
|
(['N+1 Query method call stack'] + @callers).join( "\n " )
|
data/lib/bullet/rack.rb
CHANGED
|
@@ -10,18 +10,16 @@ module Bullet
|
|
|
10
10
|
return @app.call(env) unless Bullet.enable?
|
|
11
11
|
Bullet.start_request
|
|
12
12
|
status, headers, response = @app.call(env)
|
|
13
|
-
return [status, headers, response] if file?(headers) || sse?(response) || empty?(response)
|
|
14
13
|
|
|
15
14
|
response_body = nil
|
|
16
15
|
if Bullet.notification?
|
|
17
|
-
if
|
|
16
|
+
if !file?(headers) && !sse?(headers) && !empty?(response) &&
|
|
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)
|
|
21
21
|
headers['Content-Length'] = response_body.bytesize.to_s
|
|
22
22
|
end
|
|
23
|
-
end
|
|
24
|
-
if Bullet.enable? && Bullet.notification?
|
|
25
23
|
Bullet.perform_out_of_channel_notifications(env)
|
|
26
24
|
end
|
|
27
25
|
[status, headers, response_body ? [response_body] : response]
|
|
@@ -60,8 +58,8 @@ module Bullet
|
|
|
60
58
|
headers["Content-Transfer-Encoding"] == "binary"
|
|
61
59
|
end
|
|
62
60
|
|
|
63
|
-
def sse?(
|
|
64
|
-
|
|
61
|
+
def sse?(headers)
|
|
62
|
+
headers["Content-Type"] == "text/event-stream"
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
def html_request?(headers, response)
|
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
|
@@ -16,6 +16,9 @@ module Bullet
|
|
|
16
16
|
autoload :Registry, 'bullet/registry'
|
|
17
17
|
autoload :NotificationCollector, 'bullet/notification_collector'
|
|
18
18
|
|
|
19
|
+
BULLET_DEBUG = 'BULLET_DEBUG'.freeze
|
|
20
|
+
TRUE = 'true'.freeze
|
|
21
|
+
|
|
19
22
|
if defined? Rails::Railtie
|
|
20
23
|
class BulletRailtie < Rails::Railtie
|
|
21
24
|
initializer "bullet.configure_rails_initialization" do |app|
|
|
@@ -25,11 +28,13 @@ module Bullet
|
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
class << self
|
|
28
|
-
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes
|
|
31
|
+
attr_writer :enable, :n_plus_one_query_enable, :unused_eager_loading_enable, :counter_cache_enable, :stacktrace_includes, :stacktrace_excludes
|
|
29
32
|
attr_reader :notification_collector, :whitelist
|
|
30
33
|
attr_accessor :add_footer, :orm_pathches_applied
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
|
|
36
|
+
available_notifiers << { :to => UniformNotifier }
|
|
37
|
+
delegate *available_notifiers
|
|
33
38
|
|
|
34
39
|
def raise=(should_raise)
|
|
35
40
|
UniformNotifier.raise=(should_raise ? Notification::UnoptimizedQueryError : false)
|
|
@@ -71,7 +76,12 @@ module Bullet
|
|
|
71
76
|
@stacktrace_includes || []
|
|
72
77
|
end
|
|
73
78
|
|
|
79
|
+
def stacktrace_excludes
|
|
80
|
+
@stacktrace_excludes || []
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
def add_whitelist(options)
|
|
84
|
+
reset_whitelist
|
|
75
85
|
@whitelist[options[:type]][options[:class_name].classify] ||= []
|
|
76
86
|
@whitelist[options[:type]][options[:class_name].classify] << options[:association].to_sym
|
|
77
87
|
end
|
|
@@ -81,7 +91,11 @@ module Bullet
|
|
|
81
91
|
end
|
|
82
92
|
|
|
83
93
|
def reset_whitelist
|
|
84
|
-
@whitelist
|
|
94
|
+
@whitelist ||= {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def clear_whitelist
|
|
98
|
+
@whitelist = nil
|
|
85
99
|
end
|
|
86
100
|
|
|
87
101
|
def bullet_logger=(active)
|
|
@@ -96,7 +110,7 @@ module Bullet
|
|
|
96
110
|
end
|
|
97
111
|
|
|
98
112
|
def debug(title, message)
|
|
99
|
-
puts "[Bullet][#{title}] #{message}" if ENV[
|
|
113
|
+
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
|
|
100
114
|
end
|
|
101
115
|
|
|
102
116
|
def start_request
|
|
@@ -130,7 +144,7 @@ module Bullet
|
|
|
130
144
|
end
|
|
131
145
|
|
|
132
146
|
def start?
|
|
133
|
-
Thread.current[:bullet_start]
|
|
147
|
+
enable? && Thread.current[:bullet_start]
|
|
134
148
|
end
|
|
135
149
|
|
|
136
150
|
def notification_collector
|
|
@@ -160,7 +174,7 @@ module Bullet
|
|
|
160
174
|
|
|
161
175
|
def footer_info
|
|
162
176
|
info = []
|
|
163
|
-
|
|
177
|
+
notification_collector.collection.each do |notification|
|
|
164
178
|
info << notification.short_notice
|
|
165
179
|
end
|
|
166
180
|
info
|
|
@@ -176,14 +190,17 @@ module Bullet
|
|
|
176
190
|
end
|
|
177
191
|
|
|
178
192
|
def profile
|
|
179
|
-
|
|
193
|
+
if Bullet.enable?
|
|
194
|
+
begin
|
|
195
|
+
Bullet.start_request
|
|
180
196
|
|
|
181
|
-
|
|
197
|
+
yield
|
|
182
198
|
|
|
183
|
-
|
|
184
|
-
|
|
199
|
+
Bullet.perform_out_of_channel_notifications if Bullet.notification?
|
|
200
|
+
ensure
|
|
201
|
+
Bullet.end_request
|
|
202
|
+
end
|
|
185
203
|
end
|
|
186
|
-
Bullet.end_request if Bullet.enable?
|
|
187
204
|
end
|
|
188
205
|
|
|
189
206
|
private
|
|
@@ -10,13 +10,13 @@ module Bullet
|
|
|
10
10
|
|
|
11
11
|
context ".add_counter_cache" do
|
|
12
12
|
it "should create notification if conditions met" do
|
|
13
|
-
expect(CounterCache).to receive(:conditions_met?).with(@post1
|
|
13
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(true)
|
|
14
14
|
expect(CounterCache).to receive(:create_notification).with("Post", [:comments])
|
|
15
15
|
CounterCache.add_counter_cache(@post1, [:comments])
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it "should not create notification if conditions not met" do
|
|
19
|
-
expect(CounterCache).to receive(:conditions_met?).with(@post1
|
|
19
|
+
expect(CounterCache).to receive(:conditions_met?).with(@post1, [:comments]).and_return(false)
|
|
20
20
|
expect(CounterCache).to receive(:create_notification).never
|
|
21
21
|
CounterCache.add_counter_cache(@post1, [:comments])
|
|
22
22
|
end
|
|
@@ -25,30 +25,30 @@ module Bullet
|
|
|
25
25
|
context ".add_possible_objects" do
|
|
26
26
|
it "should add possible objects" do
|
|
27
27
|
CounterCache.add_possible_objects([@post1, @post2])
|
|
28
|
-
expect(CounterCache.
|
|
29
|
-
expect(CounterCache.
|
|
28
|
+
expect(CounterCache.possible_objects).to be_include(@post1.bullet_key)
|
|
29
|
+
expect(CounterCache.possible_objects).to be_include(@post2.bullet_key)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
it "should add impossible object" do
|
|
33
33
|
CounterCache.add_impossible_object(@post1)
|
|
34
|
-
expect(CounterCache.
|
|
34
|
+
expect(CounterCache.impossible_objects).to be_include(@post1.bullet_key)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
context ".conditions_met?" do
|
|
39
39
|
it "should be true when object is possible, not impossible" do
|
|
40
40
|
CounterCache.add_possible_objects(@post1)
|
|
41
|
-
expect(CounterCache.
|
|
41
|
+
expect(CounterCache.conditions_met?(@post1, :associations)).to eq true
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
it "should be false when object is not possible" do
|
|
45
|
-
expect(CounterCache.
|
|
45
|
+
expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
it "should be true when object is possible, and impossible" do
|
|
49
49
|
CounterCache.add_possible_objects(@post1)
|
|
50
50
|
CounterCache.add_impossible_object(@post1)
|
|
51
|
-
expect(CounterCache.
|
|
51
|
+
expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -18,73 +18,88 @@ module Bullet
|
|
|
18
18
|
context ".possible?" do
|
|
19
19
|
it "should be true if possible_objects contain" do
|
|
20
20
|
NPlusOneQuery.add_possible_objects(@post)
|
|
21
|
-
expect(NPlusOneQuery.
|
|
21
|
+
expect(NPlusOneQuery.possible?(@post)).to eq true
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
context ".impossible?" do
|
|
26
26
|
it "should be true if impossible_objects contain" do
|
|
27
27
|
NPlusOneQuery.add_impossible_object(@post)
|
|
28
|
-
expect(NPlusOneQuery.
|
|
28
|
+
expect(NPlusOneQuery.impossible?(@post)).to eq true
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
context ".association?" do
|
|
33
33
|
it "should be true if object, associations pair is already existed" do
|
|
34
34
|
NPlusOneQuery.add_object_associations(@post, :association)
|
|
35
|
-
expect(NPlusOneQuery.
|
|
35
|
+
expect(NPlusOneQuery.association?(@post, :association)).to eq true
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
it "should be false if object, association pair is not existed" do
|
|
39
39
|
NPlusOneQuery.add_object_associations(@post, :association1)
|
|
40
|
-
expect(NPlusOneQuery.
|
|
40
|
+
expect(NPlusOneQuery.association?(@post, :associatio2)).to eq false
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
context ".conditions_met?" do
|
|
45
45
|
it "should be true if object is possible, not impossible and object, associations pair is not already existed" do
|
|
46
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
47
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
48
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
49
|
-
expect(NPlusOneQuery.
|
|
46
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
|
|
47
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
|
|
48
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
|
|
49
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq true
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
it "should be false if object is not possible, not impossible and object, associations pair is not already existed" do
|
|
53
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
54
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
55
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
56
|
-
expect(NPlusOneQuery.
|
|
53
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(false)
|
|
54
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
|
|
55
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
|
|
56
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
it "should be false if object is possible, but impossible and object, associations pair is not already existed" do
|
|
60
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
61
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
62
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
63
|
-
expect(NPlusOneQuery.
|
|
60
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
|
|
61
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(true)
|
|
62
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(false)
|
|
63
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
it "should be false if object is possible, not impossible and object, associations pair is already existed" do
|
|
67
|
-
allow(NPlusOneQuery).to receive(:possible?).with(@post
|
|
68
|
-
allow(NPlusOneQuery).to receive(:impossible?).with(@post
|
|
69
|
-
allow(NPlusOneQuery).to receive(:association?).with(@post
|
|
70
|
-
expect(NPlusOneQuery.
|
|
67
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post).and_return(true)
|
|
68
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post).and_return(false)
|
|
69
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post, :associations).and_return(true)
|
|
70
|
+
expect(NPlusOneQuery.conditions_met?(@post, :associations)).to eq false
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
context ".call_association" do
|
|
75
75
|
it "should create notification if conditions met" do
|
|
76
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
76
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
77
77
|
expect(NPlusOneQuery).to receive(:caller_in_project).and_return(["caller"])
|
|
78
78
|
expect(NPlusOneQuery).to receive(:create_notification).with(["caller"], "Post", :association)
|
|
79
79
|
NPlusOneQuery.call_association(@post, :association)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
it "should not create notification if conditions not met" do
|
|
83
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
83
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(false)
|
|
84
84
|
expect(NPlusOneQuery).not_to receive(:caller_in_project!)
|
|
85
85
|
expect(NPlusOneQuery).not_to receive(:create_notification).with("Post", :association)
|
|
86
86
|
NPlusOneQuery.call_association(@post, :association)
|
|
87
87
|
end
|
|
88
|
+
|
|
89
|
+
context "stacktrace_excludes" do
|
|
90
|
+
before { Bullet.stacktrace_excludes = [ 'def' ] }
|
|
91
|
+
after { Bullet.stacktrace_excludes = nil }
|
|
92
|
+
|
|
93
|
+
it "should not create notification when stacktrace contains paths that are in the exclude list" do
|
|
94
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
|
95
|
+
included_path = '/ghi/ghi.rb'
|
|
96
|
+
excluded_path = '/def/def.rb'
|
|
97
|
+
|
|
98
|
+
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_path, excluded_path])
|
|
99
|
+
expect(NPlusOneQuery).to_not receive(:create_notification)
|
|
100
|
+
NPlusOneQuery.call_association(@post, :association)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
88
103
|
end
|
|
89
104
|
|
|
90
105
|
context ".caller_in_project" do
|
|
@@ -93,7 +108,7 @@ module Bullet
|
|
|
93
108
|
not_in_project = '/def/def.rb'
|
|
94
109
|
|
|
95
110
|
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, not_in_project])
|
|
96
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
111
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
97
112
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
|
|
98
113
|
NPlusOneQuery.call_association(@post, :association)
|
|
99
114
|
end
|
|
@@ -108,7 +123,7 @@ module Bullet
|
|
|
108
123
|
excluded_gem = '/ghi/ghi.rb'
|
|
109
124
|
|
|
110
125
|
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_gem, excluded_gem])
|
|
111
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
126
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
112
127
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project, included_gem], "Post", :association)
|
|
113
128
|
NPlusOneQuery.call_association(@post, :association)
|
|
114
129
|
end
|
|
@@ -118,8 +133,8 @@ module Bullet
|
|
|
118
133
|
context ".add_possible_objects" do
|
|
119
134
|
it "should add possible objects" do
|
|
120
135
|
NPlusOneQuery.add_possible_objects([@post, @post2])
|
|
121
|
-
expect(NPlusOneQuery.
|
|
122
|
-
expect(NPlusOneQuery.
|
|
136
|
+
expect(NPlusOneQuery.possible_objects).to be_include(@post.bullet_key)
|
|
137
|
+
expect(NPlusOneQuery.possible_objects).to be_include(@post2.bullet_key)
|
|
123
138
|
end
|
|
124
139
|
|
|
125
140
|
it "should not raise error if object is nil" do
|
|
@@ -130,7 +145,7 @@ module Bullet
|
|
|
130
145
|
context ".add_impossible_object" do
|
|
131
146
|
it "should add impossible object" do
|
|
132
147
|
NPlusOneQuery.add_impossible_object(@post)
|
|
133
|
-
expect(NPlusOneQuery.
|
|
148
|
+
expect(NPlusOneQuery.impossible_objects).to be_include(@post.bullet_key)
|
|
134
149
|
end
|
|
135
150
|
end
|
|
136
151
|
end
|
|
@@ -27,5 +27,11 @@ describe Object do
|
|
|
27
27
|
expect(post.primary_key_value).to eq(post.name)
|
|
28
28
|
Post.primary_key = 'id'
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
it "should return value for multiple primary keys" do
|
|
32
|
+
post = Post.first
|
|
33
|
+
allow(Post).to receive(:primary_keys).and_return([:category_id, :writer_id])
|
|
34
|
+
expect(post.primary_key_value).to eq("#{post.category_id},#{post.writer_id}")
|
|
35
|
+
end
|
|
30
36
|
end
|
|
31
37
|
end
|
|
@@ -30,14 +30,14 @@ module Bullet
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
it "should return blank if no user available" do
|
|
33
|
-
temp_env_variable("USER","") do
|
|
33
|
+
temp_env_variable("USER", "") do
|
|
34
34
|
expect(subject).to receive(:`).with("whoami").and_return("")
|
|
35
35
|
expect(subject.whoami).to eq("")
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
it "should return blank if whoami is not available" do
|
|
40
|
-
temp_env_variable("USER","") do
|
|
40
|
+
temp_env_variable("USER", "") do
|
|
41
41
|
expect(subject).to receive(:`).with("whoami").and_raise(Errno::ENOENT)
|
|
42
42
|
expect(subject.whoami).to eq("")
|
|
43
43
|
end
|
data/spec/bullet/rack_spec.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Bullet
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
it "should change response body if notification is active" do
|
|
68
|
-
expect(Bullet).to receive(:notification?).and_return(true)
|
|
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
71
|
status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
|
|
@@ -77,7 +77,7 @@ module Bullet
|
|
|
77
77
|
response = Support::ResponseDouble.new
|
|
78
78
|
response.body = "<html><head></head><body>é</body></html>"
|
|
79
79
|
app.response = response
|
|
80
|
-
expect(Bullet).to receive(:notification?).and_return(true)
|
|
80
|
+
expect(Bullet).to receive(:notification?).and_return(true)
|
|
81
81
|
expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
|
|
82
82
|
status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
|
|
83
83
|
expect(headers["Content-Length"]).to eq("58")
|
data/spec/bullet_spec.rb
CHANGED
|
@@ -38,4 +38,51 @@ describe Bullet, focused: true do
|
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
|
+
|
|
42
|
+
describe '#start?' do
|
|
43
|
+
context 'when bullet is disabled' do
|
|
44
|
+
before(:each) do
|
|
45
|
+
Bullet.enable = false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'should not be started' do
|
|
49
|
+
expect(Bullet).not_to be_start
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '#debug' do
|
|
55
|
+
before(:each) do
|
|
56
|
+
$stdout = StringIO.new
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
after(:each) do
|
|
60
|
+
$stdout = STDOUT
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'when debug is enabled' do
|
|
64
|
+
before(:each) do
|
|
65
|
+
ENV['BULLET_DEBUG'] = 'true'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
after(:each) do
|
|
69
|
+
ENV['BULLET_DEBUG'] = 'false'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'should output debug information' do
|
|
73
|
+
Bullet.debug('debug_message', 'this is helpful information')
|
|
74
|
+
|
|
75
|
+
expect($stdout.string)
|
|
76
|
+
.to eq("[Bullet][debug_message] this is helpful information\n")
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'when debug is disabled' do
|
|
81
|
+
it 'should output debug information' do
|
|
82
|
+
Bullet.debug('debug_message', 'this is helpful information')
|
|
83
|
+
|
|
84
|
+
expect($stdout.string).to be_empty
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
41
88
|
end
|
|
@@ -335,6 +335,15 @@ if !mongoid? && active_record3?
|
|
|
335
335
|
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Comment, :post)
|
|
336
336
|
end
|
|
337
337
|
|
|
338
|
+
it "should not detect non preload association with only one comment" do
|
|
339
|
+
Comment.first.post.category.name
|
|
340
|
+
|
|
341
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
342
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
343
|
+
|
|
344
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
345
|
+
end
|
|
346
|
+
|
|
338
347
|
it "should detect non preload association with post => category" do
|
|
339
348
|
Comment.includes(:post).each do |comment|
|
|
340
349
|
comment.post.category.name
|
|
@@ -615,7 +624,7 @@ if !mongoid? && active_record3?
|
|
|
615
624
|
|
|
616
625
|
context "whitelist n plus one query" do
|
|
617
626
|
before { Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments }
|
|
618
|
-
after { Bullet.
|
|
627
|
+
after { Bullet.clear_whitelist }
|
|
619
628
|
|
|
620
629
|
it "should not detect n plus one query" do
|
|
621
630
|
Post.all.each do |post|
|
|
@@ -638,7 +647,7 @@ if !mongoid? && active_record3?
|
|
|
638
647
|
|
|
639
648
|
context "whitelist unused eager loading" do
|
|
640
649
|
before { Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments }
|
|
641
|
-
after { Bullet.
|
|
650
|
+
after { Bullet.clear_whitelist }
|
|
642
651
|
|
|
643
652
|
it "should not detect unused eager loading" do
|
|
644
653
|
Post.includes(:comments).map(&:name)
|
|
@@ -347,6 +347,15 @@ if !mongoid? && active_record4?
|
|
|
347
347
|
expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Comment, :post)
|
|
348
348
|
end
|
|
349
349
|
|
|
350
|
+
it "should not detect non preload association with only one comment" do
|
|
351
|
+
Comment.first.post.category.name
|
|
352
|
+
|
|
353
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
354
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
355
|
+
|
|
356
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
357
|
+
end
|
|
358
|
+
|
|
350
359
|
it "should detect non preload association with post => category" do
|
|
351
360
|
Comment.includes(:post).each do |comment|
|
|
352
361
|
comment.post.category.name
|
|
@@ -551,6 +560,27 @@ if !mongoid? && active_record4?
|
|
|
551
560
|
end
|
|
552
561
|
end
|
|
553
562
|
|
|
563
|
+
describe Bullet::Detector::Association, "query immediately after creation" do
|
|
564
|
+
context "document => children" do
|
|
565
|
+
it 'should not detect non preload associations' do
|
|
566
|
+
document1 = Document.new
|
|
567
|
+
document1.children.build
|
|
568
|
+
document1.save
|
|
569
|
+
|
|
570
|
+
document2 = Document.new(parent: document1)
|
|
571
|
+
document2.save
|
|
572
|
+
document2.parent
|
|
573
|
+
|
|
574
|
+
document1.children.each.first
|
|
575
|
+
|
|
576
|
+
Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
|
|
577
|
+
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
|
|
578
|
+
|
|
579
|
+
expect(Bullet::Detector::Association).to be_completely_preloading_associations
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
|
|
554
584
|
describe Bullet::Detector::Association, "STI" do
|
|
555
585
|
context "page => author" do
|
|
556
586
|
it "should detect non preload associations" do
|
|
@@ -638,7 +668,7 @@ if !mongoid? && active_record4?
|
|
|
638
668
|
|
|
639
669
|
context "whitelist n plus one query" do
|
|
640
670
|
before { Bullet.add_whitelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments }
|
|
641
|
-
after { Bullet.
|
|
671
|
+
after { Bullet.clear_whitelist }
|
|
642
672
|
|
|
643
673
|
it "should not detect n plus one query" do
|
|
644
674
|
Post.all.each do |post|
|
|
@@ -661,7 +691,7 @@ if !mongoid? && active_record4?
|
|
|
661
691
|
|
|
662
692
|
context "whitelist unused eager loading" do
|
|
663
693
|
before { Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments }
|
|
664
|
-
after { Bullet.
|
|
694
|
+
after { Bullet.clear_whitelist }
|
|
665
695
|
|
|
666
696
|
it "should not detect unused eager loading" do
|
|
667
697
|
Post.includes(:comments).map(&:name)
|