airbrake-ruby 5.0.1-java → 5.0.2-java
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/lib/airbrake-ruby.rb +1 -0
- data/lib/airbrake-ruby/config/processor.rb +4 -19
- data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +2 -7
- data/lib/airbrake-ruby/version.rb +1 -1
- data/spec/config/processor_spec.rb +0 -84
- data/spec/remote_settings/callback_spec.rb +141 -0
- data/spec/remote_settings/settings_data_spec.rb +19 -49
- data/spec/remote_settings_spec.rb +4 -4
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7353bd2b22422a376f862a0af218a5fd67bd1a5074a759ee233b74bb5986c19
|
4
|
+
data.tar.gz: 9f0debfc7e2eb97bd2dd0e263d1e40f81e3c8132d009dfc18a9cfcabf66fe31f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07ae0b29054ee4bf1a28d5aea1bc364e675290998dcad5a96ef9a218fa01173abc7577bba534bd10baf5786694aa86a3a2266ba42440947125c7a2dab4e6da5f
|
7
|
+
data.tar.gz: dc8eacdeda076da7adffa1a469ed2ffe65e3d78a810cbf38b2e5a55ba584144f343a9cfa9a3c2b1cc036eabe79198045c4dc5e8239dd948c7292f3fba1a3e3fc
|
data/lib/airbrake-ruby.rb
CHANGED
@@ -13,6 +13,7 @@ require 'airbrake-ruby/grouppable'
|
|
13
13
|
require 'airbrake-ruby/config'
|
14
14
|
require 'airbrake-ruby/config/validator'
|
15
15
|
require 'airbrake-ruby/config/processor'
|
16
|
+
require 'airbrake-ruby/remote_settings/callback'
|
16
17
|
require 'airbrake-ruby/remote_settings/settings_data'
|
17
18
|
require 'airbrake-ruby/remote_settings'
|
18
19
|
require 'airbrake-ruby/promise'
|
@@ -18,6 +18,7 @@ module Airbrake
|
|
18
18
|
@blocklist_keys = @config.blocklist_keys
|
19
19
|
@allowlist_keys = @config.allowlist_keys
|
20
20
|
@project_id = @config.project_id
|
21
|
+
@poll_callback = Airbrake::RemoteSettings::Callback.new(config)
|
21
22
|
end
|
22
23
|
|
23
24
|
# @param [Airbrake::NoticeNotifier] notifier
|
@@ -42,11 +43,9 @@ module Airbrake
|
|
42
43
|
def process_remote_configuration
|
43
44
|
return unless @project_id
|
44
45
|
|
45
|
-
RemoteSettings.poll(
|
46
|
-
@
|
47
|
-
|
48
|
-
&method(:poll_callback)
|
49
|
-
)
|
46
|
+
RemoteSettings.poll(@project_id, @config.remote_config_host) do |data|
|
47
|
+
@poll_callback.call(data)
|
48
|
+
end
|
50
49
|
end
|
51
50
|
|
52
51
|
# @param [Airbrake::NoticeNotifier] notifier
|
@@ -65,20 +64,6 @@ module Airbrake
|
|
65
64
|
notifier.add_filter(filter.new(@config.root_directory))
|
66
65
|
end
|
67
66
|
end
|
68
|
-
|
69
|
-
# @param [Airbrake::RemoteSettings::SettingsData] data
|
70
|
-
# @return [void]
|
71
|
-
def poll_callback(data)
|
72
|
-
@config.logger.debug(
|
73
|
-
"#{LOG_LABEL} applying remote settings: #{data.to_h}",
|
74
|
-
)
|
75
|
-
|
76
|
-
@config.error_host = data.error_host if data.error_host
|
77
|
-
@config.apm_host = data.apm_host if data.apm_host
|
78
|
-
|
79
|
-
@config.error_notifications = data.error_notifications?
|
80
|
-
@config.performance_stats = data.performance_stats?
|
81
|
-
end
|
82
67
|
end
|
83
68
|
end
|
84
69
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Airbrake
|
2
|
+
class RemoteSettings
|
3
|
+
# Callback is a class that provides a callback for the config poller, which
|
4
|
+
# updates the local config according to the data.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
# @since 5.0.2
|
8
|
+
class Callback
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
@orig_error_notifications = config.error_notifications
|
12
|
+
@orig_performance_stats = config.performance_stats
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Airbrake::RemoteSettings::SettingsData] data
|
16
|
+
# @return [void]
|
17
|
+
def call(data)
|
18
|
+
@config.logger.debug(
|
19
|
+
"#{LOG_LABEL} applying remote settings: #{data.to_h}",
|
20
|
+
)
|
21
|
+
|
22
|
+
@config.error_host = data.error_host if data.error_host
|
23
|
+
@config.apm_host = data.apm_host if data.apm_host
|
24
|
+
|
25
|
+
process_error_notifications(data)
|
26
|
+
process_performance_stats(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def process_error_notifications(data)
|
32
|
+
return unless @orig_error_notifications
|
33
|
+
|
34
|
+
@config.error_notifications = data.error_notifications?
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_performance_stats(data)
|
38
|
+
return unless @orig_performance_stats
|
39
|
+
|
40
|
+
@config.performance_stats = data.performance_stats?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -58,13 +58,8 @@ module Airbrake
|
|
58
58
|
# @param [String] remote_config_host
|
59
59
|
# @return [String] where the config is stored on S3.
|
60
60
|
def config_route(remote_config_host)
|
61
|
-
if @data
|
62
|
-
|
63
|
-
return format(
|
64
|
-
CONFIG_ROUTE_PATTERN,
|
65
|
-
host: @data['config_route'].chomp('/'),
|
66
|
-
project_id: @project_id,
|
67
|
-
)
|
61
|
+
if @data['config_route'] && !@data['config_route'].empty?
|
62
|
+
return remote_config_host.chomp('/') + '/' + @data['config_route']
|
68
63
|
end
|
69
64
|
|
70
65
|
format(
|
@@ -122,88 +122,4 @@ RSpec.describe Airbrake::Config::Processor do
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
end
|
125
|
-
|
126
|
-
describe "#poll_callback" do
|
127
|
-
let(:logger) { Logger.new(File::NULL) }
|
128
|
-
|
129
|
-
let(:config) do
|
130
|
-
Airbrake::Config.new(
|
131
|
-
project_id: 123,
|
132
|
-
logger: logger,
|
133
|
-
)
|
134
|
-
end
|
135
|
-
|
136
|
-
let(:data) do
|
137
|
-
instance_double(Airbrake::RemoteSettings::SettingsData)
|
138
|
-
end
|
139
|
-
|
140
|
-
before do
|
141
|
-
allow(data).to receive(:to_h)
|
142
|
-
allow(data).to receive(:error_host)
|
143
|
-
allow(data).to receive(:apm_host)
|
144
|
-
allow(data).to receive(:error_notifications?)
|
145
|
-
allow(data).to receive(:performance_stats?)
|
146
|
-
end
|
147
|
-
|
148
|
-
it "logs given data" do
|
149
|
-
expect(logger).to receive(:debug).with(/applying remote settings/)
|
150
|
-
described_class.new(config).poll_callback(data)
|
151
|
-
end
|
152
|
-
|
153
|
-
it "sets the error_notifications option" do
|
154
|
-
config.error_notifications = false
|
155
|
-
expect(data).to receive(:error_notifications?).and_return(true)
|
156
|
-
|
157
|
-
described_class.new(config).poll_callback(data)
|
158
|
-
expect(config.error_notifications).to eq(true)
|
159
|
-
end
|
160
|
-
|
161
|
-
it "sets the performance_stats option" do
|
162
|
-
config.performance_stats = false
|
163
|
-
expect(data).to receive(:performance_stats?).and_return(true)
|
164
|
-
|
165
|
-
described_class.new(config).poll_callback(data)
|
166
|
-
expect(config.performance_stats).to eq(true)
|
167
|
-
end
|
168
|
-
|
169
|
-
context "when error_host returns a value" do
|
170
|
-
it "sets the error_host option" do
|
171
|
-
config.error_host = 'http://api.airbrake.io'
|
172
|
-
allow(data).to receive(:error_host).and_return('https://api.example.com')
|
173
|
-
|
174
|
-
described_class.new(config).poll_callback(data)
|
175
|
-
expect(config.error_host).to eq('https://api.example.com')
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
context "when error_host returns nil" do
|
180
|
-
it "doesn't modify the error_host option" do
|
181
|
-
config.error_host = 'http://api.airbrake.io'
|
182
|
-
allow(data).to receive(:error_host).and_return(nil)
|
183
|
-
|
184
|
-
described_class.new(config).poll_callback(data)
|
185
|
-
expect(config.error_host).to eq('http://api.airbrake.io')
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
context "when apm_host returns a value" do
|
190
|
-
it "sets the apm_host option" do
|
191
|
-
config.apm_host = 'http://api.airbrake.io'
|
192
|
-
allow(data).to receive(:apm_host).and_return('https://api.example.com')
|
193
|
-
|
194
|
-
described_class.new(config).poll_callback(data)
|
195
|
-
expect(config.apm_host).to eq('https://api.example.com')
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
context "when apm_host returns nil" do
|
200
|
-
it "doesn't modify the apm_host option" do
|
201
|
-
config.apm_host = 'http://api.airbrake.io'
|
202
|
-
allow(data).to receive(:apm_host).and_return(nil)
|
203
|
-
|
204
|
-
described_class.new(config).poll_callback(data)
|
205
|
-
expect(config.apm_host).to eq('http://api.airbrake.io')
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
125
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
RSpec.describe Airbrake::RemoteSettings::Callback do
|
2
|
+
describe "#call" do
|
3
|
+
let(:logger) { Logger.new(File::NULL) }
|
4
|
+
|
5
|
+
let(:config) do
|
6
|
+
Airbrake::Config.new(
|
7
|
+
project_id: 123,
|
8
|
+
logger: logger,
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:data) do
|
13
|
+
instance_double(Airbrake::RemoteSettings::SettingsData)
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
allow(data).to receive(:to_h)
|
18
|
+
allow(data).to receive(:error_host)
|
19
|
+
allow(data).to receive(:apm_host)
|
20
|
+
allow(data).to receive(:error_notifications?)
|
21
|
+
allow(data).to receive(:performance_stats?)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "logs given data" do
|
25
|
+
expect(logger).to receive(:debug).with(/applying remote settings/)
|
26
|
+
described_class.new(config).call(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when the config disables error notifications" do
|
30
|
+
before do
|
31
|
+
config.error_notifications = false
|
32
|
+
allow(data).to receive(:error_notifications?).and_return(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "keeps the option disabled forever" do
|
36
|
+
callback = described_class.new(config)
|
37
|
+
|
38
|
+
callback.call(data)
|
39
|
+
expect(config.error_notifications).to eq(false)
|
40
|
+
|
41
|
+
callback.call(data)
|
42
|
+
expect(config.error_notifications).to eq(false)
|
43
|
+
|
44
|
+
callback.call(data)
|
45
|
+
expect(config.error_notifications).to eq(false)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when the config enables error notifications" do
|
50
|
+
before { config.error_notifications = true }
|
51
|
+
|
52
|
+
it "can disable and enable error notifications" do
|
53
|
+
expect(data).to receive(:error_notifications?).and_return(false)
|
54
|
+
|
55
|
+
callback = described_class.new(config)
|
56
|
+
callback.call(data)
|
57
|
+
expect(config.error_notifications).to eq(false)
|
58
|
+
|
59
|
+
expect(data).to receive(:error_notifications?).and_return(true)
|
60
|
+
callback.call(data)
|
61
|
+
expect(config.error_notifications).to eq(true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the config disables performance_stats" do
|
66
|
+
before do
|
67
|
+
config.performance_stats = false
|
68
|
+
allow(data).to receive(:performance_stats?).and_return(true)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "keeps the option disabled forever" do
|
72
|
+
callback = described_class.new(config)
|
73
|
+
|
74
|
+
callback.call(data)
|
75
|
+
expect(config.performance_stats).to eq(false)
|
76
|
+
|
77
|
+
callback.call(data)
|
78
|
+
expect(config.performance_stats).to eq(false)
|
79
|
+
|
80
|
+
callback.call(data)
|
81
|
+
expect(config.performance_stats).to eq(false)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when the config enables performance stats" do
|
86
|
+
before { config.performance_stats = true }
|
87
|
+
|
88
|
+
it "can disable and enable performance_stats" do
|
89
|
+
expect(data).to receive(:performance_stats?).and_return(false)
|
90
|
+
|
91
|
+
callback = described_class.new(config)
|
92
|
+
callback.call(data)
|
93
|
+
expect(config.performance_stats).to eq(false)
|
94
|
+
|
95
|
+
expect(data).to receive(:performance_stats?).and_return(true)
|
96
|
+
callback.call(data)
|
97
|
+
expect(config.performance_stats).to eq(true)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when error_host returns a value" do
|
102
|
+
it "sets the error_host option" do
|
103
|
+
config.error_host = 'http://api.airbrake.io'
|
104
|
+
allow(data).to receive(:error_host).and_return('https://api.example.com')
|
105
|
+
|
106
|
+
described_class.new(config).call(data)
|
107
|
+
expect(config.error_host).to eq('https://api.example.com')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when error_host returns nil" do
|
112
|
+
it "doesn't modify the error_host option" do
|
113
|
+
config.error_host = 'http://api.airbrake.io'
|
114
|
+
allow(data).to receive(:error_host).and_return(nil)
|
115
|
+
|
116
|
+
described_class.new(config).call(data)
|
117
|
+
expect(config.error_host).to eq('http://api.airbrake.io')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "when apm_host returns a value" do
|
122
|
+
it "sets the apm_host option" do
|
123
|
+
config.apm_host = 'http://api.airbrake.io'
|
124
|
+
allow(data).to receive(:apm_host).and_return('https://api.example.com')
|
125
|
+
|
126
|
+
described_class.new(config).call(data)
|
127
|
+
expect(config.apm_host).to eq('https://api.example.com')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when apm_host returns nil" do
|
132
|
+
it "doesn't modify the apm_host option" do
|
133
|
+
config.apm_host = 'http://api.airbrake.io'
|
134
|
+
allow(data).to receive(:apm_host).and_return(nil)
|
135
|
+
|
136
|
+
described_class.new(config).call(data)
|
137
|
+
expect(config.apm_host).to eq('http://api.airbrake.io')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -13,7 +13,7 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
|
|
13
13
|
|
14
14
|
expect(settings_data.interval).to eq(123)
|
15
15
|
expect(settings_data.config_route(''))
|
16
|
-
.to eq('abc
|
16
|
+
.to eq('/abc')
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -60,78 +60,48 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
describe "#config_route" do
|
63
|
-
let(:host) { '
|
63
|
+
let(:host) { 'http://example.com/' }
|
64
64
|
|
65
|
-
context "when
|
66
|
-
|
67
|
-
|
68
|
-
{ 'config_route' => 'http://example.com/' }
|
69
|
-
end
|
70
|
-
|
71
|
-
it "returns the route with the host" do
|
72
|
-
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
73
|
-
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
74
|
-
)
|
75
|
-
end
|
65
|
+
context "when remote config specifies a config route" do
|
66
|
+
let(:data) do
|
67
|
+
{ 'config_route' => '123/cfg/321/cfg.json' }
|
76
68
|
end
|
77
69
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
it "returns the route with the host" do
|
84
|
-
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
85
|
-
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
86
|
-
)
|
87
|
-
end
|
70
|
+
it "returns the config route with the provided location" do
|
71
|
+
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
72
|
+
'http://example.com/123/cfg/321/cfg.json',
|
73
|
+
)
|
88
74
|
end
|
89
75
|
end
|
90
76
|
|
91
|
-
context "when
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
expect(described_class.new(project_id, {}).config_route(host)).to eq(
|
97
|
-
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
98
|
-
)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
context "and when the remote host doesn't with a slash" do
|
103
|
-
let(:host) { 'http://example.com' }
|
104
|
-
|
105
|
-
it "returns the route with the given host" do
|
106
|
-
expect(described_class.new(project_id, {}).config_route(host)).to eq(
|
107
|
-
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
108
|
-
)
|
109
|
-
end
|
77
|
+
context "when remote config DOES NOT specify a config route" do
|
78
|
+
it "returns the config route with the default location" do
|
79
|
+
expect(described_class.new(project_id, {}).config_route(host)).to eq(
|
80
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
81
|
+
)
|
110
82
|
end
|
111
83
|
end
|
112
84
|
|
113
|
-
context "when
|
85
|
+
context "when a config route is specified but is set to nil" do
|
114
86
|
let(:data) do
|
115
87
|
{ 'config_route' => nil }
|
116
88
|
end
|
117
89
|
|
118
|
-
it "returns the route with the
|
90
|
+
it "returns the config route with the default location" do
|
119
91
|
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
120
|
-
|
121
|
-
"2020-06-18/config/#{project_id}/config.json",
|
92
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
122
93
|
)
|
123
94
|
end
|
124
95
|
end
|
125
96
|
|
126
|
-
context "when
|
97
|
+
context "when a config route is specified but is set to an empty string" do
|
127
98
|
let(:data) do
|
128
99
|
{ 'config_route' => '' }
|
129
100
|
end
|
130
101
|
|
131
102
|
it "returns the route with the default instead" do
|
132
103
|
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
133
|
-
|
134
|
-
"2020-06-18/config/#{project_id}/config.json",
|
104
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
135
105
|
)
|
136
106
|
end
|
137
107
|
end
|
@@ -175,16 +175,16 @@ RSpec.describe Airbrake::RemoteSettings do
|
|
175
175
|
end
|
176
176
|
|
177
177
|
context "when a config route is specified in the returned data" do
|
178
|
-
let(:
|
179
|
-
|
178
|
+
let(:new_config_route) do
|
179
|
+
'213/config/111/config.json'
|
180
180
|
end
|
181
181
|
|
182
182
|
let(:body) do
|
183
|
-
{ 'config_route' =>
|
183
|
+
{ 'config_route' => new_config_route, 'poll_sec' => 0.1 }
|
184
184
|
end
|
185
185
|
|
186
186
|
let!(:new_stub) do
|
187
|
-
stub_request(:get, Regexp.new(
|
187
|
+
stub_request(:get, Regexp.new(new_config_route))
|
188
188
|
.to_return(status: 200, body: body.to_json)
|
189
189
|
end
|
190
190
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: airbrake-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.2
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Airbrake Technologies, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbtree-jruby
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- lib/airbrake-ruby/query.rb
|
80
80
|
- lib/airbrake-ruby/queue.rb
|
81
81
|
- lib/airbrake-ruby/remote_settings.rb
|
82
|
+
- lib/airbrake-ruby/remote_settings/callback.rb
|
82
83
|
- lib/airbrake-ruby/remote_settings/settings_data.rb
|
83
84
|
- lib/airbrake-ruby/request.rb
|
84
85
|
- lib/airbrake-ruby/response.rb
|
@@ -135,6 +136,7 @@ files:
|
|
135
136
|
- spec/promise_spec.rb
|
136
137
|
- spec/query_spec.rb
|
137
138
|
- spec/queue_spec.rb
|
139
|
+
- spec/remote_settings/callback_spec.rb
|
138
140
|
- spec/remote_settings/settings_data_spec.rb
|
139
141
|
- spec/remote_settings_spec.rb
|
140
142
|
- spec/request_spec.rb
|
@@ -215,6 +217,7 @@ test_files:
|
|
215
217
|
- spec/notice_notifier/options_spec.rb
|
216
218
|
- spec/filter_chain_spec.rb
|
217
219
|
- spec/remote_settings/settings_data_spec.rb
|
220
|
+
- spec/remote_settings/callback_spec.rb
|
218
221
|
- spec/response_spec.rb
|
219
222
|
- spec/queue_spec.rb
|
220
223
|
- spec/code_hunk_spec.rb
|