bullet 4.13.1 → 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 +29 -1
- data/CHANGELOG.md +53 -2
- 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 +17 -0
- data/README.md +29 -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 +214 -0
- data/lib/bullet/dependency.rb +12 -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 -27
- data/lib/bullet/detector/unused_eager_loading.rb +2 -2
- data/lib/bullet/ext/object.rb +4 -2
- data/lib/bullet/mongoid5x.rb +56 -0
- data/lib/bullet/notification/base.rb +7 -11
- data/lib/bullet/notification/n_plus_one_query.rb +6 -4
- data/lib/bullet/rack.rb +20 -13
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +24 -11
- 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 +4 -29
- 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/counter_cache_spec.rb +8 -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 +2 -0
- data/update.sh +15 -0
- metadata +14 -7
|
@@ -18,6 +18,10 @@ module Bullet
|
|
|
18
18
|
raise NoMethodError.new("no method body defined")
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def call_stack_messages
|
|
22
|
+
""
|
|
23
|
+
end
|
|
24
|
+
|
|
21
25
|
def whoami
|
|
22
26
|
@user ||= ENV['USER'].presence || (`whoami`.chomp rescue "")
|
|
23
27
|
if @user.present?
|
|
@@ -28,15 +32,7 @@ module Bullet
|
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def body_with_caller
|
|
31
|
-
body
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def standard_notice
|
|
35
|
-
@standard_notifice ||= title + "\n" + body
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def full_notice
|
|
39
|
-
[whoami.presence, url, title, body_with_caller].compact.join("\n")
|
|
35
|
+
"#{body}\n#{call_stack_messages}\n"
|
|
40
36
|
end
|
|
41
37
|
|
|
42
38
|
def notify_inline
|
|
@@ -48,7 +44,7 @@ module Bullet
|
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
def short_notice
|
|
51
|
-
[whoami.presence, url, title, body].compact.join("
|
|
47
|
+
[whoami.presence, url, title, body].compact.join(" ")
|
|
52
48
|
end
|
|
53
49
|
|
|
54
50
|
def notification_data
|
|
@@ -74,7 +70,7 @@ module Bullet
|
|
|
74
70
|
end
|
|
75
71
|
|
|
76
72
|
def associations_str
|
|
77
|
-
":
|
|
73
|
+
":includes => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
|
|
78
74
|
end
|
|
79
75
|
end
|
|
80
76
|
end
|
|
@@ -7,10 +7,6 @@ module Bullet
|
|
|
7
7
|
@callers = callers
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def body_with_caller
|
|
11
|
-
"#{body}\n#{call_stack_messages}"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
10
|
def body
|
|
15
11
|
"#{klazz_associations_str}\n Add to your finder: #{associations_str}"
|
|
16
12
|
end
|
|
@@ -19,6 +15,12 @@ module Bullet
|
|
|
19
15
|
"N+1 Query #{@path ? "in #{@path}" : 'detected'}"
|
|
20
16
|
end
|
|
21
17
|
|
|
18
|
+
def notification_data
|
|
19
|
+
super.merge(
|
|
20
|
+
:backtrace => @callers
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
22
24
|
protected
|
|
23
25
|
def call_stack_messages
|
|
24
26
|
(['N+1 Query method call stack'] + @callers).join( "\n " )
|
data/lib/bullet/rack.rb
CHANGED
|
@@ -10,17 +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
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
if !file?(headers) && !sse?(headers) && !empty?(response) &&
|
|
17
|
+
status == 200 && !response_body(response).frozen? && html_request?(headers, response)
|
|
18
|
+
response_body = response_body(response)
|
|
19
|
+
append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
|
20
|
+
append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
|
20
21
|
headers['Content-Length'] = response_body.bytesize.to_s
|
|
21
22
|
end
|
|
22
|
-
end
|
|
23
|
-
if Bullet.enable? && Bullet.notification?
|
|
24
23
|
Bullet.perform_out_of_channel_notifications(env)
|
|
25
24
|
end
|
|
26
25
|
[status, headers, response_body ? [response_body] : response]
|
|
@@ -42,16 +41,25 @@ module Bullet
|
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
def
|
|
46
|
-
response_body
|
|
44
|
+
def append_to_html_body(response_body, content)
|
|
45
|
+
if response_body.include?('</body>')
|
|
46
|
+
position = response_body.rindex('</body>')
|
|
47
|
+
response_body.insert(position, content)
|
|
48
|
+
else
|
|
49
|
+
response_body << content
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def footer_note
|
|
54
|
+
"<div #{footer_div_attributes}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
|
|
47
55
|
end
|
|
48
56
|
|
|
49
57
|
def file?(headers)
|
|
50
58
|
headers["Content-Transfer-Encoding"] == "binary"
|
|
51
59
|
end
|
|
52
60
|
|
|
53
|
-
def sse?(
|
|
54
|
-
|
|
61
|
+
def sse?(headers)
|
|
62
|
+
headers["Content-Type"] == "text/event-stream"
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
def html_request?(headers, response)
|
|
@@ -67,9 +75,9 @@ module Bullet
|
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
private
|
|
70
|
-
def
|
|
78
|
+
def footer_div_attributes
|
|
71
79
|
<<EOF
|
|
72
|
-
style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
|
80
|
+
data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
|
|
73
81
|
-moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
|
|
74
82
|
-moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
|
|
75
83
|
padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
|
|
@@ -78,4 +86,3 @@ EOF
|
|
|
78
86
|
end
|
|
79
87
|
end
|
|
80
88
|
end
|
|
81
|
-
|
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|
|
|
@@ -29,7 +32,9 @@ module Bullet
|
|
|
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)
|
|
@@ -72,6 +77,7 @@ module Bullet
|
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
def add_whitelist(options)
|
|
80
|
+
reset_whitelist
|
|
75
81
|
@whitelist[options[:type]][options[:class_name].classify] ||= []
|
|
76
82
|
@whitelist[options[:type]][options[:class_name].classify] << options[:association].to_sym
|
|
77
83
|
end
|
|
@@ -81,7 +87,11 @@ module Bullet
|
|
|
81
87
|
end
|
|
82
88
|
|
|
83
89
|
def reset_whitelist
|
|
84
|
-
@whitelist
|
|
90
|
+
@whitelist ||= {:n_plus_one_query => {}, :unused_eager_loading => {}, :counter_cache => {}}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def clear_whitelist
|
|
94
|
+
@whitelist = nil
|
|
85
95
|
end
|
|
86
96
|
|
|
87
97
|
def bullet_logger=(active)
|
|
@@ -96,7 +106,7 @@ module Bullet
|
|
|
96
106
|
end
|
|
97
107
|
|
|
98
108
|
def debug(title, message)
|
|
99
|
-
puts "[Bullet][#{title}] #{message}" if ENV[
|
|
109
|
+
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
|
|
100
110
|
end
|
|
101
111
|
|
|
102
112
|
def start_request
|
|
@@ -130,7 +140,7 @@ module Bullet
|
|
|
130
140
|
end
|
|
131
141
|
|
|
132
142
|
def start?
|
|
133
|
-
Thread.current[:bullet_start]
|
|
143
|
+
enable? && Thread.current[:bullet_start]
|
|
134
144
|
end
|
|
135
145
|
|
|
136
146
|
def notification_collector
|
|
@@ -153,14 +163,14 @@ module Bullet
|
|
|
153
163
|
|
|
154
164
|
def perform_out_of_channel_notifications(env = {})
|
|
155
165
|
for_each_active_notifier_with_notification do |notification|
|
|
156
|
-
notification.url =
|
|
166
|
+
notification.url = env['REQUEST_URI']
|
|
157
167
|
notification.notify_out_of_channel
|
|
158
168
|
end
|
|
159
169
|
end
|
|
160
170
|
|
|
161
171
|
def footer_info
|
|
162
172
|
info = []
|
|
163
|
-
|
|
173
|
+
notification_collector.collection.each do |notification|
|
|
164
174
|
info << notification.short_notice
|
|
165
175
|
end
|
|
166
176
|
info
|
|
@@ -176,14 +186,17 @@ module Bullet
|
|
|
176
186
|
end
|
|
177
187
|
|
|
178
188
|
def profile
|
|
179
|
-
|
|
189
|
+
if Bullet.enable?
|
|
190
|
+
begin
|
|
191
|
+
Bullet.start_request
|
|
180
192
|
|
|
181
|
-
|
|
193
|
+
yield
|
|
182
194
|
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
Bullet.perform_out_of_channel_notifications if Bullet.notification?
|
|
196
|
+
ensure
|
|
197
|
+
Bullet.end_request
|
|
198
|
+
end
|
|
185
199
|
end
|
|
186
|
-
Bullet.end_request if Bullet.enable?
|
|
187
200
|
end
|
|
188
201
|
|
|
189
202
|
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,69 +18,69 @@ 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)
|
|
@@ -93,7 +93,7 @@ module Bullet
|
|
|
93
93
|
not_in_project = '/def/def.rb'
|
|
94
94
|
|
|
95
95
|
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, not_in_project])
|
|
96
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
96
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
97
97
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
|
|
98
98
|
NPlusOneQuery.call_association(@post, :association)
|
|
99
99
|
end
|
|
@@ -108,7 +108,7 @@ module Bullet
|
|
|
108
108
|
excluded_gem = '/ghi/ghi.rb'
|
|
109
109
|
|
|
110
110
|
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_gem, excluded_gem])
|
|
111
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post
|
|
111
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
|
112
112
|
expect(NPlusOneQuery).to receive(:create_notification).with([in_project, included_gem], "Post", :association)
|
|
113
113
|
NPlusOneQuery.call_association(@post, :association)
|
|
114
114
|
end
|
|
@@ -118,8 +118,8 @@ module Bullet
|
|
|
118
118
|
context ".add_possible_objects" do
|
|
119
119
|
it "should add possible objects" do
|
|
120
120
|
NPlusOneQuery.add_possible_objects([@post, @post2])
|
|
121
|
-
expect(NPlusOneQuery.
|
|
122
|
-
expect(NPlusOneQuery.
|
|
121
|
+
expect(NPlusOneQuery.possible_objects).to be_include(@post.bullet_key)
|
|
122
|
+
expect(NPlusOneQuery.possible_objects).to be_include(@post2.bullet_key)
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
it "should not raise error if object is nil" do
|
|
@@ -130,7 +130,7 @@ module Bullet
|
|
|
130
130
|
context ".add_impossible_object" do
|
|
131
131
|
it "should add impossible object" do
|
|
132
132
|
NPlusOneQuery.add_impossible_object(@post)
|
|
133
|
-
expect(NPlusOneQuery.
|
|
133
|
+
expect(NPlusOneQuery.impossible_objects).to be_include(@post.bullet_key)
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
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
|
|
@@ -56,33 +56,8 @@ module Bullet
|
|
|
56
56
|
context "#body_with_caller" do
|
|
57
57
|
it "should return body" do
|
|
58
58
|
allow(subject).to receive(:body).and_return("body")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
context "#standard_notice" do
|
|
64
|
-
it "should return title + body" do
|
|
65
|
-
allow(subject).to receive(:title).and_return("title")
|
|
66
|
-
allow(subject).to receive(:body).and_return("body")
|
|
67
|
-
expect(subject.standard_notice).to eq("title\nbody")
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
context "#full_notice" do
|
|
72
|
-
it "should return whoami + url + title + body_with_caller" do
|
|
73
|
-
allow(subject).to receive(:whoami).and_return("whoami")
|
|
74
|
-
allow(subject).to receive(:url).and_return("url")
|
|
75
|
-
allow(subject).to receive(:title).and_return("title")
|
|
76
|
-
allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
|
|
77
|
-
expect(subject.full_notice).to eq("whoami\nurl\ntitle\nbody_with_caller")
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it "should return url + title + body_with_caller" do
|
|
81
|
-
allow(subject).to receive(:whoami).and_return("")
|
|
82
|
-
allow(subject).to receive(:url).and_return("url")
|
|
83
|
-
allow(subject).to receive(:title).and_return("title")
|
|
84
|
-
allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
|
|
85
|
-
expect(subject.full_notice).to eq("url\ntitle\nbody_with_caller")
|
|
59
|
+
allow(subject).to receive(:call_stack_messages).and_return("call_stack_messages")
|
|
60
|
+
expect(subject.body_with_caller).to eq("body\ncall_stack_messages\n")
|
|
86
61
|
end
|
|
87
62
|
end
|
|
88
63
|
|
|
@@ -5,9 +5,9 @@ module Bullet
|
|
|
5
5
|
describe NPlusOneQuery do
|
|
6
6
|
subject { NPlusOneQuery.new([["caller1", "caller2"]], Post, [:comments, :votes], "path") }
|
|
7
7
|
|
|
8
|
-
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :
|
|
9
|
-
it { expect([
|
|
10
|
-
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :
|
|
8
|
+
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n") }
|
|
9
|
+
it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n" ]) }
|
|
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("N+1 Query in path") }
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -5,7 +5,7 @@ module Bullet
|
|
|
5
5
|
describe UnusedEagerLoading do
|
|
6
6
|
subject { UnusedEagerLoading.new(Post, [:comments, :votes], "path") }
|
|
7
7
|
|
|
8
|
-
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :
|
|
8
|
+
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :includes => [:comments, :votes]") }
|
|
9
9
|
it { expect(subject.title).to eq("Unused Eager Loading in path") }
|
|
10
10
|
end
|
|
11
11
|
end
|
data/spec/bullet/rack_spec.rb
CHANGED
|
@@ -65,19 +65,19 @@ 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"}])
|
|
72
72
|
expect(headers["Content-Length"]).to eq("56")
|
|
73
|
-
expect(response).to eq(["<html><head></head><body></body></html
|
|
73
|
+
expect(response).to eq(["<html><head></head><body><bullet></bullet></body></html>"])
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
it "should set the right Content-Length if response body contains accents" do
|
|
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)
|