bullet 6.1.0 → 7.0.7

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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +82 -0
  3. data/CHANGELOG.md +66 -0
  4. data/Gemfile.rails-6.0 +1 -1
  5. data/Gemfile.rails-6.1 +15 -0
  6. data/Gemfile.rails-7.0 +10 -0
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +41 -27
  9. data/lib/bullet/active_job.rb +5 -1
  10. data/lib/bullet/active_record41.rb +1 -0
  11. data/lib/bullet/active_record42.rb +1 -0
  12. data/lib/bullet/active_record5.rb +10 -8
  13. data/lib/bullet/active_record52.rb +32 -25
  14. data/lib/bullet/active_record60.rb +30 -23
  15. data/lib/bullet/active_record61.rb +274 -0
  16. data/lib/bullet/active_record70.rb +284 -0
  17. data/lib/bullet/bullet_xhr.js +18 -17
  18. data/lib/bullet/dependency.rb +16 -0
  19. data/lib/bullet/detector/association.rb +8 -0
  20. data/lib/bullet/detector/base.rb +2 -1
  21. data/lib/bullet/detector/counter_cache.rb +2 -2
  22. data/lib/bullet/detector/n_plus_one_query.rb +24 -13
  23. data/lib/bullet/detector/unused_eager_loading.rb +3 -3
  24. data/lib/bullet/mongoid4x.rb +1 -1
  25. data/lib/bullet/mongoid5x.rb +1 -1
  26. data/lib/bullet/mongoid6x.rb +1 -1
  27. data/lib/bullet/mongoid7x.rb +32 -17
  28. data/lib/bullet/notification.rb +2 -1
  29. data/lib/bullet/rack.rb +64 -23
  30. data/lib/bullet/registry/call_stack.rb +12 -0
  31. data/lib/bullet/registry.rb +1 -0
  32. data/lib/bullet/stack_trace_filter.rb +15 -15
  33. data/lib/bullet/version.rb +1 -1
  34. data/lib/bullet.rb +35 -29
  35. data/lib/generators/bullet/install_generator.rb +22 -25
  36. data/perf/benchmark.rb +4 -1
  37. data/spec/bullet/detector/counter_cache_spec.rb +1 -1
  38. data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -33
  39. data/spec/bullet/detector/unused_eager_loading_spec.rb +15 -10
  40. data/spec/bullet/ext/object_spec.rb +1 -1
  41. data/spec/bullet/notification/base_spec.rb +4 -4
  42. data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
  43. data/spec/bullet/rack_spec.rb +152 -9
  44. data/spec/bullet/stack_trace_filter_spec.rb +26 -0
  45. data/spec/bullet_spec.rb +15 -15
  46. data/spec/integration/active_record/association_spec.rb +95 -12
  47. data/spec/integration/counter_cache_spec.rb +4 -4
  48. data/spec/integration/mongoid/association_spec.rb +4 -4
  49. data/spec/models/attachment.rb +5 -0
  50. data/spec/models/deal.rb +5 -0
  51. data/spec/models/folder.rb +2 -1
  52. data/spec/models/group.rb +2 -1
  53. data/spec/models/page.rb +2 -1
  54. data/spec/models/post.rb +2 -0
  55. data/spec/models/role.rb +7 -0
  56. data/spec/models/submission.rb +1 -0
  57. data/spec/models/user.rb +2 -0
  58. data/spec/models/writer.rb +2 -1
  59. data/spec/spec_helper.rb +0 -2
  60. data/spec/support/mongo_seed.rb +1 -0
  61. data/spec/support/sqlite_seed.rb +38 -0
  62. data/test.sh +2 -0
  63. metadata +20 -7
  64. data/.travis.yml +0 -33
@@ -10,40 +10,37 @@ module Bullet
10
10
 
11
11
  def enable_in_development
12
12
  environment(nil, env: 'development') do
13
- <<-"FILE"
14
-
15
- config.after_initialize do
16
- Bullet.enable = true
17
- Bullet.alert = true
18
- Bullet.bullet_logger = true
19
- Bullet.console = true
20
- # Bullet.growl = true
21
- Bullet.rails_logger = true
22
- Bullet.add_footer = true
23
- end
13
+ <<~FILE
14
+ config.after_initialize do
15
+ Bullet.enable = true
16
+ Bullet.alert = true
17
+ Bullet.bullet_logger = true
18
+ Bullet.console = true
19
+ Bullet.rails_logger = true
20
+ Bullet.add_footer = true
21
+ end
22
+
24
23
  FILE
25
- .strip
26
24
  end
27
25
 
28
26
  say 'Enabled bullet in config/environments/development.rb'
29
27
  end
30
28
 
31
29
  def enable_in_test
32
- if yes?('Would you like to enable bullet in test environment? (y/n)')
33
- environment(nil, env: 'test') do
34
- <<-"FILE"
35
-
36
- config.after_initialize do
37
- Bullet.enable = true
38
- Bullet.bullet_logger = true
39
- Bullet.raise = true # raise an error if n+1 query occurs
40
- end
41
- FILE
42
- .strip
43
- end
30
+ return unless yes?('Would you like to enable bullet in test environment? (y/n)')
44
31
 
45
- say 'Enabled bullet in config/environments/test.rb'
32
+ environment(nil, env: 'test') do
33
+ <<~FILE
34
+ config.after_initialize do
35
+ Bullet.enable = true
36
+ Bullet.bullet_logger = true
37
+ Bullet.raise = true # raise an error if n+1 query occurs
38
+ end
39
+
40
+ FILE
46
41
  end
42
+
43
+ say 'Enabled bullet in config/environments/test.rb'
47
44
  end
48
45
  end
49
46
  end
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', database: 'bullet_benchmark', server: '/tmp/mysql.socket', username: 'root'
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) }
@@ -47,7 +47,7 @@ module Bullet
47
47
  expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
48
48
  end
49
49
 
50
- it 'should be true when object is possible, and impossible' do
50
+ it 'should be false when object is possible, and impossible' do
51
51
  CounterCache.add_possible_objects(@post1)
52
52
  CounterCache.add_impossible_object(@post1)
53
53
  expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
@@ -39,7 +39,7 @@ module Bullet
39
39
 
40
40
  it 'should be false if object, association pair is not existed' do
41
41
  NPlusOneQuery.add_object_associations(@post, :association1)
42
- expect(NPlusOneQuery.association?(@post, :associatio2)).to eq false
42
+ expect(NPlusOneQuery.association?(@post, :association2)).to eq false
43
43
  end
44
44
  end
45
45
 
@@ -127,38 +127,6 @@ module Bullet
127
127
  end
128
128
  end
129
129
 
130
- context '.caller_in_project' do
131
- it 'should include only paths that are in the project' do
132
- in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
133
- not_in_project = OpenStruct.new(absolute_path: '/def/def.rb')
134
-
135
- expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, not_in_project])
136
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
137
- expect(NPlusOneQuery).to receive(:create_notification).with([in_project], 'Post', :association)
138
- NPlusOneQuery.call_association(@post, :association)
139
- end
140
-
141
- context 'stacktrace_includes' do
142
- before { Bullet.stacktrace_includes = ['def', /xyz/] }
143
- after { Bullet.stacktrace_includes = nil }
144
-
145
- it 'should include paths that are in the stacktrace_include list' do
146
- in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
147
- included_gems = [OpenStruct.new(absolute_path: '/def/def.rb'), OpenStruct.new(absolute_path: 'xyz/xyz.rb')]
148
- excluded_gem = OpenStruct.new(absolute_path: '/ghi/ghi.rb')
149
-
150
- expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, *included_gems, excluded_gem])
151
- expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
152
- expect(NPlusOneQuery).to receive(:create_notification).with(
153
- [in_project, *included_gems],
154
- 'Post',
155
- :association
156
- )
157
- NPlusOneQuery.call_association(@post, :association)
158
- end
159
- end
160
- end
161
-
162
130
  context '.add_possible_objects' do
163
131
  it 'should add possible objects' do
164
132
  NPlusOneQuery.add_possible_objects([@post, @post2])
@@ -13,27 +13,27 @@ module Bullet
13
13
 
14
14
  context '.call_associations' do
15
15
  it 'should get empty array if eager_loadings' do
16
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(%i[association]))).to be_empty
16
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
17
17
  end
18
18
 
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(%i[association]))).to eq(
23
- %i[association]
22
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
23
+ [:association]
24
24
  )
25
25
  end
26
26
 
27
27
  it 'should not get call associations if not exist in call_object_associations' do
28
28
  UnusedEagerLoading.add_eager_loadings([@post], :association)
29
- expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(%i[association]))).to be_empty
29
+ expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
30
30
  end
31
31
  end
32
32
 
33
33
  context '.diff_object_associations' do
34
34
  it 'should return associations not exist in call_association' do
35
- expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(%i[association]))).to eq(
36
- %i[association]
35
+ expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
36
+ [:association]
37
37
  )
38
38
  end
39
39
 
@@ -41,7 +41,7 @@ module Bullet
41
41
  UnusedEagerLoading.add_eager_loadings([@post], :association)
42
42
  UnusedEagerLoading.add_call_object_associations(@post, :association)
43
43
  expect(
44
- UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(%i[association]))
44
+ UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
45
45
  ).to be_empty
46
46
  end
47
47
  end
@@ -51,7 +51,7 @@ module Bullet
51
51
  it 'should create notification if object_association_diff is not empty' do
52
52
  UnusedEagerLoading.add_object_associations(@post, :association)
53
53
  allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
54
- expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', %i[association])
54
+ expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', [:association])
55
55
  UnusedEagerLoading.check_unused_preload_associations
56
56
  end
57
57
 
@@ -60,11 +60,16 @@ module Bullet
60
60
  UnusedEagerLoading.add_eager_loadings([@post], :association)
61
61
  UnusedEagerLoading.add_call_object_associations(@post, :association)
62
62
  expect(
63
- UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(%i[association]))
63
+ UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
64
64
  ).to be_empty
65
- expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', %i[association])
65
+ expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
66
66
  UnusedEagerLoading.check_unused_preload_associations
67
67
  end
68
+
69
+ it 'should create call stack for notification' do
70
+ UnusedEagerLoading.add_object_associations(@post, :association)
71
+ expect(UnusedEagerLoading.send(:call_stacks).registry).not_to be_empty
72
+ end
68
73
  end
69
74
 
70
75
  context '.add_eager_loadings' do
@@ -10,7 +10,7 @@ describe Object do
10
10
  end
11
11
 
12
12
  if mongoid?
13
- it 'should return class with namesapce and id composition' do
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
@@ -74,8 +74,8 @@ module Bullet
74
74
  it 'should send full_notice to notifier' do
75
75
  notifier = double
76
76
  allow(subject).to receive(:notifier).and_return(notifier)
77
- allow(subject).to receive(:notification_data).and_return(foo: :bar)
78
- expect(notifier).to receive(:inline_notify).with(foo: :bar)
77
+ allow(subject).to receive(:notification_data).and_return({ foo: :bar })
78
+ expect(notifier).to receive(:inline_notify).with({ foo: :bar })
79
79
  subject.notify_inline
80
80
  end
81
81
  end
@@ -84,8 +84,8 @@ module Bullet
84
84
  it 'should send full_out_of_channel to notifier' do
85
85
  notifier = double
86
86
  allow(subject).to receive(:notifier).and_return(notifier)
87
- allow(subject).to receive(:notification_data).and_return(foo: :bar)
88
- expect(notifier).to receive(:out_of_channel_notify).with(foo: :bar)
87
+ allow(subject).to receive(:notification_data).and_return({ foo: :bar })
88
+ expect(notifier).to receive(:out_of_channel_notify).with({ foo: :bar })
89
89
  subject.notify_out_of_channel
90
90
  end
91
91
  end
@@ -21,9 +21,7 @@ module Bullet
21
21
  )
22
22
  end
23
23
  it do
24
- expect(subject.body).to eq(
25
- " Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])"
26
- )
24
+ expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])")
27
25
  end
28
26
  it { expect(subject.title).to eq('USE eager loading in path') }
29
27
  end
@@ -31,12 +31,6 @@ module Bullet
31
31
  response = double(body: '<html><head></head><body></body></html>')
32
32
  expect(middleware).not_to be_html_request(headers, response)
33
33
  end
34
-
35
- it "should be false if response body doesn't contain html tag" do
36
- headers = { 'Content-Type' => 'text/html' }
37
- response = double(body: '<div>Partial</div>')
38
- expect(middleware).not_to be_html_request(headers, response)
39
- end
40
34
  end
41
35
 
42
36
  context 'empty?' do
@@ -54,6 +48,11 @@ module Bullet
54
48
  response = double(body: '')
55
49
  expect(middleware).to be_empty(response)
56
50
  end
51
+
52
+ it 'should be true if no response body' do
53
+ response = double
54
+ expect(middleware).to be_empty(response)
55
+ end
57
56
  end
58
57
 
59
58
  context '#call' do
@@ -67,9 +66,8 @@ module Bullet
67
66
 
68
67
  it 'should change response body if notification is active' do
69
68
  expect(Bullet).to receive(:notification?).and_return(true)
70
- allow(Bullet).to receive(:skip_html_injection?).and_return(false)
69
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
71
70
  expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
72
- expect(middleware).to receive(:xhr_script).and_return('')
73
71
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
74
72
  _, headers, response = middleware.call('Content-Type' => 'text/html')
75
73
  expect(headers['Content-Length']).to eq('56')
@@ -81,9 +79,140 @@ module Bullet
81
79
  response.body = '<html><head></head><body>é</body></html>'
82
80
  app.response = response
83
81
  expect(Bullet).to receive(:notification?).and_return(true)
82
+ allow(Bullet).to receive(:console_enabled?).and_return(true)
84
83
  expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
85
84
  _, headers, response = middleware.call('Content-Type' => 'text/html')
86
- expect(headers['Content-Length']).to eq((58 + middleware.send(:xhr_script).length).to_s)
85
+ expect(headers['Content-Length']).to eq('58')
86
+ end
87
+
88
+ context 'with injection notifiers' do
89
+ before do
90
+ expect(Bullet).to receive(:notification?).and_return(true)
91
+ allow(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
92
+ allow(middleware).to receive(:xhr_script).and_return('<script></script>')
93
+ allow(middleware).to receive(:footer_note).and_return('footer')
94
+ expect(Bullet).to receive(:perform_out_of_channel_notifications)
95
+ end
96
+
97
+ it 'should change response body if add_footer is true' do
98
+ expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
99
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
100
+
101
+ expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
102
+ expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
103
+ end
104
+
105
+ it 'should change response body for html safe string if add_footer is true' do
106
+ expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
107
+ app.response =
108
+ Support::ResponseDouble.new.tap do |response|
109
+ response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
110
+ end
111
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
112
+
113
+ expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
114
+ expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
115
+ end
116
+
117
+ it 'should add the footer-text header for non-html requests when add_footer is true' do
118
+ allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
119
+ allow(Bullet).to receive(:footer_info).and_return(['footer text'])
120
+ app.headers = { 'Content-Type' => 'application/json' }
121
+ _, headers, _response = middleware.call({})
122
+ expect(headers).to include('X-bullet-footer-text' => '["footer text"]')
123
+ end
124
+
125
+ it 'should change response body if console_enabled is true' do
126
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
127
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
128
+ expect(headers['Content-Length']).to eq('56')
129
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
130
+ end
131
+
132
+ it 'should include CSP nonce in inline script if console_enabled and a CSP is applied' do
133
+ allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
134
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
135
+ allow(middleware).to receive(:xhr_script).and_call_original
136
+
137
+ nonce = '+t9/wTlgG6xbHxXYUaDNzQ=='
138
+ app.headers = {
139
+ 'Content-Type' => 'text/html',
140
+ 'Content-Security-Policy' => "default-src 'self' https:; script-src 'self' https: 'nonce-#{nonce}'"
141
+ }
142
+
143
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
144
+
145
+ size = 56 + middleware.send(:footer_note).length + middleware.send(:xhr_script, nonce).length
146
+ expect(headers['Content-Length']).to eq(size.to_s)
147
+ end
148
+
149
+ it 'should change response body for html safe string if console_enabled is true' do
150
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
151
+ app.response =
152
+ Support::ResponseDouble.new.tap do |response|
153
+ response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
154
+ end
155
+ _, headers, response = middleware.call('Content-Type' => 'text/html')
156
+ expect(headers['Content-Length']).to eq('56')
157
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
158
+ end
159
+
160
+ it 'should add headers for non-html requests when console_enabled is true' do
161
+ allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
162
+ allow(Bullet).to receive(:text_notifications).and_return(['text notifications'])
163
+ app.headers = { 'Content-Type' => 'application/json' }
164
+ _, headers, _response = middleware.call({})
165
+ expect(headers).to include('X-bullet-console-text' => '["text notifications"]')
166
+ end
167
+
168
+ it "shouldn't change response body unnecessarily" do
169
+ expected_response = Support::ResponseDouble.new 'Actual body'
170
+ app.response = expected_response
171
+ _, _, response = middleware.call({})
172
+ expect(response).to eq(expected_response)
173
+ end
174
+
175
+ it "shouldn't add headers unnecessarily" do
176
+ app.headers = { 'Content-Type' => 'application/json' }
177
+ _, headers, _response = middleware.call({})
178
+ expect(headers).not_to include('X-bullet-footer-text')
179
+ expect(headers).not_to include('X-bullet-console-text')
180
+ end
181
+
182
+ context 'when skip_http_headers is enabled' do
183
+ before do
184
+ allow(Bullet).to receive(:skip_http_headers).and_return(true)
185
+ end
186
+
187
+ it 'should include the footer but not the xhr script tag if add_footer is true' do
188
+ expect(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
189
+ _, headers, response = middleware.call({})
190
+
191
+ expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
192
+ expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet></body></html>])
193
+ end
194
+
195
+ it 'should not include the xhr script tag if console_enabled is true' do
196
+ expect(Bullet).to receive(:console_enabled?).and_return(true)
197
+ _, headers, response = middleware.call({})
198
+ expect(headers['Content-Length']).to eq('56')
199
+ expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
200
+ end
201
+
202
+ it 'should not add the footer-text header for non-html requests when add_footer is true' do
203
+ allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
204
+ app.headers = { 'Content-Type' => 'application/json' }
205
+ _, headers, _response = middleware.call({})
206
+ expect(headers).not_to include('X-bullet-footer-text')
207
+ end
208
+
209
+ it 'should not add headers for non-html requests when console_enabled is true' do
210
+ allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
211
+ app.headers = { 'Content-Type' => 'application/json' }
212
+ _, headers, _response = middleware.call({})
213
+ expect(headers).not_to include('X-bullet-console-text')
214
+ end
215
+ end
87
216
  end
88
217
 
89
218
  context 'when skip_html_injection is enabled' do
@@ -148,6 +277,20 @@ module Bullet
148
277
  expect(middleware.response_body(response)).to eq body_string
149
278
  end
150
279
  end
280
+
281
+ begin
282
+ require 'rack/files'
283
+
284
+ context 'when `response` is a Rack::Files::Iterator' do
285
+ let(:response) { instance_double(::Rack::Files::Iterator) }
286
+ before { allow(response).to receive(:is_a?).with(::Rack::Files::Iterator) { true } }
287
+
288
+ it 'should return nil' do
289
+ expect(middleware.response_body(response)).to be_nil
290
+ end
291
+ end
292
+ rescue LoadError
293
+ end
151
294
  end
152
295
  end
153
296
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module Bullet
6
+ RSpec.describe StackTraceFilter do
7
+ let(:dummy_class) { Class.new { extend StackTraceFilter } }
8
+ let(:root_path) { Dir.pwd }
9
+ let(:bundler_path) { Bundler.bundle_path }
10
+
11
+ describe '#caller_in_project' do
12
+ it 'gets the caller in the project' do
13
+ expect(dummy_class).to receive(:call_stacks).and_return({
14
+ 'Post:1' => [
15
+ File.join(root_path, 'lib/bullet.rb'),
16
+ File.join(root_path, 'vendor/uniform_notifier.rb'),
17
+ File.join(bundler_path, 'rack.rb')
18
+ ]
19
+ })
20
+ expect(dummy_class.caller_in_project('Post:1')).to eq([
21
+ File.join(root_path, 'lib/bullet.rb')
22
+ ])
23
+ end
24
+ end
25
+ end
26
+ end
data/spec/bullet_spec.rb CHANGED
@@ -74,31 +74,31 @@ describe Bullet, focused: true do
74
74
  end
75
75
  end
76
76
 
77
- describe '#add_whitelist' do
77
+ describe '#add_safelist' do
78
78
  context "for 'special' class names" do
79
- it 'is added to the whitelist successfully' do
80
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
81
- expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
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
82
  end
83
83
  end
84
84
  end
85
85
 
86
- describe '#delete_whitelist' do
86
+ describe '#delete_safelist' do
87
87
  context "for 'special' class names" do
88
- it 'is deleted from the whitelist successfully' do
89
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
90
- Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
91
- expect(Bullet.whitelist[:n_plus_one_query]).to eq({})
88
+ it 'is deleted from the safelist successfully' do
89
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
90
+ Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
91
+ expect(Bullet.safelist[:n_plus_one_query]).to eq({})
92
92
  end
93
93
  end
94
94
 
95
95
  context 'when exists multiple definitions' do
96
- it 'is deleted from the whitelist successfully' do
97
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
98
- Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
99
- Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
100
- expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to include :department
101
- expect(Bullet.get_whitelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
96
+ it 'is deleted from the safelist successfully' do
97
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
98
+ Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
99
+ Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
100
+ expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
101
+ expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
102
102
  end
103
103
  end
104
104
  end