bullet 6.1.2 → 7.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/.github/workflows/main.yml +82 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile.rails-7.0 +10 -0
- data/README.md +10 -7
- data/lib/bullet/active_record41.rb +1 -0
- data/lib/bullet/active_record42.rb +1 -0
- data/lib/bullet/active_record52.rb +22 -17
- data/lib/bullet/active_record60.rb +22 -17
- data/lib/bullet/active_record61.rb +22 -17
- data/lib/bullet/active_record70.rb +261 -0
- data/lib/bullet/bullet_xhr.js +1 -0
- data/lib/bullet/dependency.rb +10 -0
- data/lib/bullet/detector/base.rb +2 -1
- data/lib/bullet/detector/counter_cache.rb +1 -1
- data/lib/bullet/detector/n_plus_one_query.rb +3 -3
- data/lib/bullet/detector/unused_eager_loading.rb +1 -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/mongoid7x.rb +24 -7
- data/lib/bullet/notification.rb +2 -1
- data/lib/bullet/rack.rb +5 -3
- data/lib/bullet/stack_trace_filter.rb +7 -9
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +68 -22
- data/perf/benchmark.rb +4 -1
- data/spec/bullet/detector/unused_eager_loading_spec.rb +6 -2
- data/spec/bullet/ext/object_spec.rb +1 -1
- data/spec/bullet/rack_spec.rb +88 -17
- data/spec/bullet_spec.rb +39 -10
- data/spec/integration/active_record/association_spec.rb +53 -8
- data/spec/integration/counter_cache_spec.rb +4 -4
- data/spec/integration/mongoid/association_spec.rb +1 -1
- data/spec/models/attachment.rb +5 -0
- data/spec/models/deal.rb +5 -0
- data/spec/models/folder.rb +2 -1
- data/spec/models/group.rb +2 -1
- data/spec/models/page.rb +2 -1
- data/spec/models/post.rb +2 -0
- data/spec/models/submission.rb +1 -0
- data/spec/models/user.rb +1 -0
- data/spec/models/writer.rb +2 -1
- data/spec/spec_helper.rb +0 -2
- data/spec/support/mongo_seed.rb +1 -0
- data/spec/support/sqlite_seed.rb +20 -0
- data/test.sh +2 -0
- metadata +10 -4
- data/.travis.yml +0 -33
data/lib/bullet/detector/base.rb
CHANGED
@@ -54,7 +54,7 @@ module Bullet
|
|
54
54
|
private
|
55
55
|
|
56
56
|
def create_notification(klazz, associations)
|
57
|
-
notify_associations = Array(associations) - Bullet.
|
57
|
+
notify_associations = Array(associations) - Bullet.get_safelist_associations(:counter_cache, klazz)
|
58
58
|
|
59
59
|
if notify_associations.present?
|
60
60
|
notice = Bullet::Notification::CounterCache.new klazz, notify_associations
|
@@ -35,6 +35,7 @@ module Bullet
|
|
35
35
|
|
36
36
|
objects = Array(object_or_objects)
|
37
37
|
return if objects.map(&:bullet_primary_key_value).compact.empty?
|
38
|
+
return if objects.all? { |obj| obj.class.name =~ /^HABTM_/ }
|
38
39
|
|
39
40
|
Bullet.debug(
|
40
41
|
'Detector::NPlusOneQuery#add_possible_objects',
|
@@ -84,8 +85,7 @@ module Bullet
|
|
84
85
|
# associations == v comparison order is important here because
|
85
86
|
# v variable might be a squeel node where :== method is redefined,
|
86
87
|
# so it does not compare values at all and return unexpected results
|
87
|
-
result =
|
88
|
-
v.is_a?(Hash) ? v.key?(associations) : associations == v
|
88
|
+
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
|
89
89
|
return true if result
|
90
90
|
end
|
91
91
|
|
@@ -95,7 +95,7 @@ module Bullet
|
|
95
95
|
private
|
96
96
|
|
97
97
|
def create_notification(callers, klazz, associations)
|
98
|
-
notify_associations = Array(associations) - Bullet.
|
98
|
+
notify_associations = Array(associations) - Bullet.get_safelist_associations(:n_plus_one_query, klazz)
|
99
99
|
|
100
100
|
if notify_associations.present?
|
101
101
|
notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
|
@@ -65,7 +65,7 @@ module Bullet
|
|
65
65
|
private
|
66
66
|
|
67
67
|
def create_notification(callers, klazz, associations)
|
68
|
-
notify_associations = Array(associations) - Bullet.
|
68
|
+
notify_associations = Array(associations) - Bullet.get_safelist_associations(:unused_eager_loading, klazz)
|
69
69
|
|
70
70
|
if notify_associations.present?
|
71
71
|
notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
|
data/lib/bullet/mongoid4x.rb
CHANGED
data/lib/bullet/mongoid5x.rb
CHANGED
data/lib/bullet/mongoid6x.rb
CHANGED
data/lib/bullet/mongoid7x.rb
CHANGED
@@ -25,14 +25,31 @@ module Bullet
|
|
25
25
|
def each(&block)
|
26
26
|
return to_enum unless block_given?
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
first_document = nil
|
29
|
+
document_count = 0
|
30
|
+
|
31
|
+
origin_each do |document|
|
32
|
+
document_count += 1
|
33
|
+
|
34
|
+
if document_count == 1
|
35
|
+
first_document = document
|
36
|
+
elsif document_count == 2
|
37
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
|
38
|
+
yield(first_document)
|
39
|
+
first_document = nil
|
40
|
+
yield(document)
|
41
|
+
else
|
42
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
|
43
|
+
yield(document)
|
44
|
+
end
|
34
45
|
end
|
35
|
-
|
46
|
+
|
47
|
+
if document_count == 1
|
48
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
|
49
|
+
yield(first_document)
|
50
|
+
end
|
51
|
+
|
52
|
+
self
|
36
53
|
end
|
37
54
|
|
38
55
|
def eager_load(docs)
|
data/lib/bullet/notification.rb
CHANGED
data/lib/bullet/rack.rb
CHANGED
@@ -22,9 +22,11 @@ module Bullet
|
|
22
22
|
response_body = response_body(response)
|
23
23
|
response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
24
24
|
response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
25
|
-
|
25
|
+
if Bullet.add_footer && !Bullet.skip_http_headers
|
26
|
+
response_body = append_to_html_body(response_body, xhr_script)
|
27
|
+
end
|
26
28
|
headers['Content-Length'] = response_body.bytesize.to_s
|
27
|
-
|
29
|
+
elsif !Bullet.skip_http_headers
|
28
30
|
set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
|
29
31
|
set_header(headers, 'X-bullet-console-text', Bullet.text_notifications) if Bullet.console_enabled?
|
30
32
|
end
|
@@ -82,7 +84,7 @@ module Bullet
|
|
82
84
|
def response_body(response)
|
83
85
|
if response.respond_to?(:body)
|
84
86
|
Array === response.body ? response.body.first : response.body
|
85
|
-
|
87
|
+
elsif response.respond_to?(:first)
|
86
88
|
response.first
|
87
89
|
end
|
88
90
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "bundler"
|
2
3
|
|
3
4
|
module Bullet
|
4
5
|
module StackTraceFilter
|
5
6
|
VENDOR_PATH = '/vendor'
|
7
|
+
IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
|
6
8
|
|
7
9
|
def caller_in_project
|
8
10
|
vendor_root = Bullet.app_root + VENDOR_PATH
|
@@ -10,8 +12,9 @@ module Bullet
|
|
10
12
|
select_caller_locations do |location|
|
11
13
|
caller_path = location_as_path(location)
|
12
14
|
caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
|
13
|
-
!caller_path.include?(bundler_path) ||
|
14
|
-
|
15
|
+
!caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern|
|
16
|
+
pattern_matches?(location, include_pattern)
|
17
|
+
}
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
@@ -47,20 +50,15 @@ module Bullet
|
|
47
50
|
end
|
48
51
|
|
49
52
|
def location_as_path(location)
|
50
|
-
|
53
|
+
IS_RUBY_19 ? location : location.absolute_path.to_s
|
51
54
|
end
|
52
55
|
|
53
56
|
def select_caller_locations
|
54
|
-
if
|
57
|
+
if IS_RUBY_19
|
55
58
|
caller.select { |caller_path| yield caller_path }
|
56
59
|
else
|
57
60
|
caller_locations.select { |location| yield location }
|
58
61
|
end
|
59
62
|
end
|
60
|
-
|
61
|
-
def ruby_19?
|
62
|
-
@ruby_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0') if @ruby_19.nil?
|
63
|
-
@ruby_19
|
64
|
-
end
|
65
63
|
end
|
66
64
|
end
|
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
@@ -20,9 +20,6 @@ module Bullet
|
|
20
20
|
autoload :Registry, 'bullet/registry'
|
21
21
|
autoload :NotificationCollector, 'bullet/notification_collector'
|
22
22
|
|
23
|
-
BULLET_DEBUG = 'BULLET_DEBUG'
|
24
|
-
TRUE = 'true'
|
25
|
-
|
26
23
|
if defined?(Rails::Railtie)
|
27
24
|
class BulletRailtie < Rails::Railtie
|
28
25
|
initializer 'bullet.configure_rails_initialization' do |app|
|
@@ -38,10 +35,11 @@ module Bullet
|
|
38
35
|
:stacktrace_includes,
|
39
36
|
:stacktrace_excludes,
|
40
37
|
:skip_html_injection
|
41
|
-
attr_reader :
|
42
|
-
attr_accessor :add_footer, :orm_patches_applied
|
38
|
+
attr_reader :safelist
|
39
|
+
attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers
|
43
40
|
|
44
|
-
available_notifiers =
|
41
|
+
available_notifiers =
|
42
|
+
UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
|
45
43
|
available_notifiers_options = { to: UniformNotifier }
|
46
44
|
delegate(*available_notifiers, **available_notifiers_options)
|
47
45
|
|
@@ -59,7 +57,7 @@ module Bullet
|
|
59
57
|
@enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
|
60
58
|
|
61
59
|
if enable?
|
62
|
-
|
60
|
+
reset_safelist
|
63
61
|
unless orm_patches_applied
|
64
62
|
self.orm_patches_applied = true
|
65
63
|
Bullet::Mongoid.enable if mongoid?
|
@@ -72,8 +70,9 @@ module Bullet
|
|
72
70
|
!!@enable
|
73
71
|
end
|
74
72
|
|
73
|
+
# Rails.root might be nil if `railties` is a dependency on a project that does not use Rails
|
75
74
|
def app_root
|
76
|
-
(defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
|
75
|
+
@app_root ||= (defined?(::Rails.root) && !::Rails.root.nil? ? Rails.root.to_s : Dir.pwd).to_s
|
77
76
|
end
|
78
77
|
|
79
78
|
def n_plus_one_query_enable?
|
@@ -89,36 +88,81 @@ module Bullet
|
|
89
88
|
end
|
90
89
|
|
91
90
|
def stacktrace_includes
|
92
|
-
@stacktrace_includes
|
91
|
+
@stacktrace_includes ||= []
|
93
92
|
end
|
94
93
|
|
95
94
|
def stacktrace_excludes
|
96
|
-
@stacktrace_excludes
|
95
|
+
@stacktrace_excludes ||= []
|
96
|
+
end
|
97
|
+
|
98
|
+
def add_safelist(options)
|
99
|
+
reset_safelist
|
100
|
+
@safelist[options[:type]][options[:class_name]] ||= []
|
101
|
+
@safelist[options[:type]][options[:class_name]] << options[:association].to_sym
|
102
|
+
end
|
103
|
+
|
104
|
+
def delete_safelist(options)
|
105
|
+
reset_safelist
|
106
|
+
@safelist[options[:type]][options[:class_name]] ||= []
|
107
|
+
@safelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
|
108
|
+
@safelist[options[:type]].delete_if { |_key, val| val.empty? }
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_safelist_associations(type, class_name)
|
112
|
+
Array(@safelist[type][class_name])
|
113
|
+
end
|
114
|
+
|
115
|
+
def reset_safelist
|
116
|
+
@safelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
|
117
|
+
end
|
118
|
+
|
119
|
+
def clear_safelist
|
120
|
+
@safelist = nil
|
97
121
|
end
|
98
122
|
|
99
123
|
def add_whitelist(options)
|
100
|
-
|
101
|
-
|
102
|
-
|
124
|
+
ActiveSupport::Deprecation.warn(<<~WARN.strip
|
125
|
+
add_whitelist is deprecated in favor of add_safelist. It will be removed from the next major release.
|
126
|
+
WARN
|
127
|
+
)
|
128
|
+
|
129
|
+
add_safelist(options)
|
103
130
|
end
|
104
131
|
|
105
132
|
def delete_whitelist(options)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
133
|
+
ActiveSupport::Deprecation.warn(<<~WARN.strip
|
134
|
+
delete_whitelist is deprecated in favor of delete_safelist. It will be removed from the next major release.
|
135
|
+
WARN
|
136
|
+
)
|
137
|
+
|
138
|
+
delete_safelist(options)
|
110
139
|
end
|
111
140
|
|
112
141
|
def get_whitelist_associations(type, class_name)
|
113
|
-
|
142
|
+
ActiveSupport::Deprecation.warn(<<~WARN.strip
|
143
|
+
get_whitelist_associations is deprecated in favor of get_safelist_associations. It will be removed from the next major release.
|
144
|
+
WARN
|
145
|
+
)
|
146
|
+
|
147
|
+
get_safelist_associations(type, class_name)
|
114
148
|
end
|
115
149
|
|
116
150
|
def reset_whitelist
|
117
|
-
|
151
|
+
ActiveSupport::Deprecation.warn(<<~WARN.strip
|
152
|
+
reset_whitelist is deprecated in favor of reset_safelist. It will be removed from the next major release.
|
153
|
+
WARN
|
154
|
+
)
|
155
|
+
|
156
|
+
reset_safelist
|
118
157
|
end
|
119
158
|
|
120
159
|
def clear_whitelist
|
121
|
-
|
160
|
+
ActiveSupport::Deprecation.warn(<<~WARN.strip
|
161
|
+
clear_whitelist is deprecated in favor of clear_safelist. It will be removed from the next major release.
|
162
|
+
WARN
|
163
|
+
)
|
164
|
+
|
165
|
+
clear_safelist
|
122
166
|
end
|
123
167
|
|
124
168
|
def bullet_logger=(active)
|
@@ -132,7 +176,7 @@ module Bullet
|
|
132
176
|
end
|
133
177
|
|
134
178
|
def debug(title, message)
|
135
|
-
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] ==
|
179
|
+
puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
|
136
180
|
end
|
137
181
|
|
138
182
|
def start_request
|
@@ -241,7 +285,9 @@ module Bullet
|
|
241
285
|
end
|
242
286
|
|
243
287
|
def inject_into_page?
|
244
|
-
|
288
|
+
return false if defined?(@skip_html_injection) && @skip_html_injection
|
289
|
+
|
290
|
+
console_enabled? || add_footer
|
245
291
|
end
|
246
292
|
|
247
293
|
private
|
data/perf/benchmark.rb
CHANGED
@@ -30,7 +30,10 @@ end
|
|
30
30
|
|
31
31
|
# create database bullet_benchmark;
|
32
32
|
ActiveRecord::Base.establish_connection(
|
33
|
-
adapter: 'mysql2',
|
33
|
+
adapter: 'mysql2',
|
34
|
+
database: 'bullet_benchmark',
|
35
|
+
server: '/tmp/mysql.socket',
|
36
|
+
username: 'root'
|
34
37
|
)
|
35
38
|
|
36
39
|
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
@@ -19,7 +19,9 @@ module Bullet
|
|
19
19
|
it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
|
20
20
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
21
21
|
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
22
|
-
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
22
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
23
|
+
[:association]
|
24
|
+
)
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'should not get call associations if not exist in call_object_associations' do
|
@@ -30,7 +32,9 @@ module Bullet
|
|
30
32
|
|
31
33
|
context '.diff_object_associations' do
|
32
34
|
it 'should return associations not exist in call_association' do
|
33
|
-
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
35
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
36
|
+
[:association]
|
37
|
+
)
|
34
38
|
end
|
35
39
|
|
36
40
|
it 'should return empty if associations exist in call_association' do
|
@@ -10,7 +10,7 @@ describe Object do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
if mongoid?
|
13
|
-
it 'should return class with
|
13
|
+
it 'should return class with namespace and id composition' do
|
14
14
|
post = Mongoid::Post.first
|
15
15
|
expect(post.bullet_key).to eq("Mongoid::Post:#{post.id}")
|
16
16
|
end
|
data/spec/bullet/rack_spec.rb
CHANGED
@@ -69,7 +69,6 @@ module Bullet
|
|
69
69
|
expect(Bullet).to receive(:notification?).and_return(true)
|
70
70
|
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
71
71
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
72
|
-
expect(middleware).to receive(:xhr_script).and_return('')
|
73
72
|
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
74
73
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
75
74
|
expect(headers['Content-Length']).to eq('56')
|
@@ -84,37 +83,44 @@ module Bullet
|
|
84
83
|
allow(Bullet).to receive(:console_enabled?).and_return(true)
|
85
84
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
86
85
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
87
|
-
expect(headers['Content-Length']).to eq(
|
86
|
+
expect(headers['Content-Length']).to eq('58')
|
88
87
|
end
|
89
88
|
|
90
89
|
context 'with injection notifiers' do
|
91
90
|
before do
|
92
91
|
expect(Bullet).to receive(:notification?).and_return(true)
|
93
92
|
allow(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
94
|
-
allow(middleware).to receive(:xhr_script).and_return('')
|
93
|
+
allow(middleware).to receive(:xhr_script).and_return('<script></script>')
|
95
94
|
allow(middleware).to receive(:footer_note).and_return('footer')
|
96
95
|
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
97
96
|
end
|
98
97
|
|
99
98
|
it 'should change response body if add_footer is true' do
|
100
|
-
expect(Bullet).to receive(:add_footer).
|
99
|
+
expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
|
101
100
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
102
101
|
|
103
|
-
expect(headers['Content-Length']).to eq((
|
104
|
-
expect(response
|
105
|
-
expect(response.first).to include('<bullet></bullet><')
|
102
|
+
expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
|
103
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
|
106
104
|
end
|
107
105
|
|
108
106
|
it 'should change response body for html safe string if add_footer is true' do
|
109
|
-
expect(Bullet).to receive(:add_footer).
|
110
|
-
app.response =
|
111
|
-
|
112
|
-
|
107
|
+
expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
|
108
|
+
app.response =
|
109
|
+
Support::ResponseDouble.new.tap do |response|
|
110
|
+
response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
|
111
|
+
end
|
113
112
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
114
113
|
|
115
|
-
expect(headers['Content-Length']).to eq((
|
116
|
-
expect(response
|
117
|
-
|
114
|
+
expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
|
115
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should add the footer-text header for non-html requests when add_footer is true' do
|
119
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
120
|
+
allow(Bullet).to receive(:footer_info).and_return(['footer text'])
|
121
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
122
|
+
_, headers, _response = middleware.call({})
|
123
|
+
expect(headers).to include('X-bullet-footer-text' => '["footer text"]')
|
118
124
|
end
|
119
125
|
|
120
126
|
it 'should change response body if console_enabled is true' do
|
@@ -126,20 +132,71 @@ module Bullet
|
|
126
132
|
|
127
133
|
it 'should change response body for html safe string if console_enabled is true' do
|
128
134
|
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
129
|
-
app.response =
|
130
|
-
|
131
|
-
|
135
|
+
app.response =
|
136
|
+
Support::ResponseDouble.new.tap do |response|
|
137
|
+
response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
|
138
|
+
end
|
132
139
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
133
140
|
expect(headers['Content-Length']).to eq('56')
|
134
141
|
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
135
142
|
end
|
136
143
|
|
144
|
+
it 'should add headers for non-html requests when console_enabled is true' do
|
145
|
+
allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
|
146
|
+
allow(Bullet).to receive(:text_notifications).and_return(['text notifications'])
|
147
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
148
|
+
_, headers, _response = middleware.call({})
|
149
|
+
expect(headers).to include('X-bullet-console-text' => '["text notifications"]')
|
150
|
+
end
|
151
|
+
|
137
152
|
it "shouldn't change response body unnecessarily" do
|
138
153
|
expected_response = Support::ResponseDouble.new 'Actual body'
|
139
154
|
app.response = expected_response
|
140
155
|
_, _, response = middleware.call({})
|
141
156
|
expect(response).to eq(expected_response)
|
142
157
|
end
|
158
|
+
|
159
|
+
it "shouldn't add headers unnecessarily" do
|
160
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
161
|
+
_, headers, _response = middleware.call({})
|
162
|
+
expect(headers).not_to include('X-bullet-footer-text')
|
163
|
+
expect(headers).not_to include('X-bullet-console-text')
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when skip_http_headers is enabled' do
|
167
|
+
before do
|
168
|
+
allow(Bullet).to receive(:skip_http_headers).and_return(true)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'should include the footer but not the xhr script tag if add_footer is true' do
|
172
|
+
expect(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
173
|
+
_, headers, response = middleware.call({})
|
174
|
+
|
175
|
+
expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
|
176
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet></body></html>])
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should not include the xhr script tag if console_enabled is true' do
|
180
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
181
|
+
_, headers, response = middleware.call({})
|
182
|
+
expect(headers['Content-Length']).to eq('56')
|
183
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should not add the footer-text header for non-html requests when add_footer is true' do
|
187
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
188
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
189
|
+
_, headers, _response = middleware.call({})
|
190
|
+
expect(headers).not_to include('X-bullet-footer-text')
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should not add headers for non-html requests when console_enabled is true' do
|
194
|
+
allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
|
195
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
196
|
+
_, headers, _response = middleware.call({})
|
197
|
+
expect(headers).not_to include('X-bullet-console-text')
|
198
|
+
end
|
199
|
+
end
|
143
200
|
end
|
144
201
|
|
145
202
|
context 'when skip_html_injection is enabled' do
|
@@ -204,6 +261,20 @@ module Bullet
|
|
204
261
|
expect(middleware.response_body(response)).to eq body_string
|
205
262
|
end
|
206
263
|
end
|
264
|
+
|
265
|
+
begin
|
266
|
+
require 'rack/files'
|
267
|
+
|
268
|
+
context 'when `response` is a Rack::Files::Iterator' do
|
269
|
+
let(:response) { instance_double(::Rack::Files::Iterator) }
|
270
|
+
before { allow(response).to receive(:is_a?).with(::Rack::Files::Iterator) { true } }
|
271
|
+
|
272
|
+
it 'should return nil' do
|
273
|
+
expect(middleware.response_body(response)).to be_nil
|
274
|
+
end
|
275
|
+
end
|
276
|
+
rescue LoadError
|
277
|
+
end
|
207
278
|
end
|
208
279
|
end
|
209
280
|
end
|
data/spec/bullet_spec.rb
CHANGED
@@ -74,31 +74,60 @@ describe Bullet, focused: true do
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
+
describe '#add_safelist' do
|
78
|
+
context "for 'special' class names" do
|
79
|
+
it 'is added to the safelist successfully' do
|
80
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
81
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
77
86
|
describe '#add_whitelist' do
|
78
87
|
context "for 'special' class names" do
|
79
|
-
it 'is added to the
|
88
|
+
it 'is added to the safelist successfully' do
|
80
89
|
Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
81
|
-
expect(Bullet.
|
90
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#delete_safelist' do
|
96
|
+
context "for 'special' class names" do
|
97
|
+
it 'is deleted from the safelist successfully' do
|
98
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
99
|
+
Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
100
|
+
expect(Bullet.safelist[:n_plus_one_query]).to eq({})
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when exists multiple definitions' do
|
105
|
+
it 'is deleted from the safelist successfully' do
|
106
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
107
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
108
|
+
Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
109
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
110
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
|
82
111
|
end
|
83
112
|
end
|
84
113
|
end
|
85
114
|
|
86
115
|
describe '#delete_whitelist' do
|
87
116
|
context "for 'special' class names" do
|
88
|
-
it 'is deleted from the
|
89
|
-
Bullet.
|
117
|
+
it 'is deleted from the safelist successfully' do
|
118
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
90
119
|
Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
91
|
-
expect(Bullet.
|
120
|
+
expect(Bullet.safelist[:n_plus_one_query]).to eq({})
|
92
121
|
end
|
93
122
|
end
|
94
123
|
|
95
124
|
context 'when exists multiple definitions' do
|
96
|
-
it 'is deleted from the
|
97
|
-
Bullet.
|
98
|
-
Bullet.
|
125
|
+
it 'is deleted from the safelist successfully' do
|
126
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
127
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
99
128
|
Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
100
|
-
expect(Bullet.
|
101
|
-
expect(Bullet.
|
129
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
130
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
|
102
131
|
end
|
103
132
|
end
|
104
133
|
end
|