ndd-url_checker 0.1.1

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.
@@ -0,0 +1,100 @@
1
+ require 'cod'
2
+ require 'logging'
3
+ require 'ndd/url_checker/abstract_url_checker'
4
+ require 'ndd/url_checker/blocking_url_checker'
5
+ require 'ndd/url_checker/status'
6
+
7
+ module NDD
8
+ module UrlChecker
9
+
10
+ # An URL checker using forks to parallelize processing. To be used with MRI.
11
+ # @author David DIDIER
12
+ class ForkedUrlChecker < AbstractUrlChecker
13
+
14
+ attr_reader :delegate
15
+ attr_reader :parallelism
16
+
17
+ # Create a new instance.
18
+ # @param [AbstractUrlChecker] delegate_checker defaults to {NDD::UrlChecker::BlockingUrlChecker}.
19
+ # @param [Fixnum] parallelism the number of processes.
20
+ def initialize(delegate_checker=nil, parallelism=10)
21
+ @logger = Logging.logger[self]
22
+ @delegate = delegate_checker || BlockingUrlChecker.new
23
+ @parallelism = parallelism
24
+ end
25
+
26
+ def check(*urls)
27
+ return delegate.check(*urls) if urls.size < 2
28
+ process(urls, :check)
29
+ end
30
+
31
+ def validate(*urls)
32
+ return delegate.validate(*urls) if urls.size < 2
33
+ process(urls, :validate)
34
+ end
35
+
36
+
37
+ private
38
+
39
+ def process(urls, method)
40
+ # for receiving results
41
+ result_pipe = Cod.pipe
42
+ # partition the URLs, but not too much :)
43
+ url_slices = partition(urls, [parallelism, urls.size].min)
44
+ # and distribute them among the workers
45
+ pids = url_slices.each_with_index.map do |url_slice, index|
46
+ fork { Worker.new(index, result_pipe, delegate).send(method, url_slice) }
47
+ end
48
+
49
+ # read back the results
50
+ results = urls.reduce({}) do |hash, _|
51
+ result = result_pipe.get
52
+ hash.merge!(result)
53
+ @logger.debug("Processed URLs #{hash.size}/#{urls.size}")
54
+ hash
55
+ end
56
+
57
+ # kill all the workers
58
+ pids.each { |pid| Process.kill(:TERM, pid) }
59
+
60
+ results
61
+ end
62
+
63
+ # Evenly distributes data into buckets. For example:
64
+ #  partition([1, 2, 3], 2) => [[1, 3], [2]]
65
+ #  partition([1, 2, 3, 4, 5, 6], 3) => [[1, 4], [2, 5], [3, 6]]
66
+ def partition(data, buckets)
67
+ Array.new.tap do |slices|
68
+ buckets.times.each { |_| slices << Array.new }
69
+ data.each_with_index { |element, index| slices[index % buckets] << element }
70
+ end
71
+ end
72
+
73
+
74
+ # A simple worker class processing URL one by one.
75
+ class Worker
76
+ def initialize(id, result_pipe, url_checker)
77
+ @logger = Logging.logger[self]
78
+ @id = id
79
+ @result_pipe = result_pipe
80
+ @url_checker = url_checker
81
+ end
82
+
83
+ def check(urls)
84
+ @logger.debug("[worker #{@id}] Checking #{urls.size} URLs")
85
+ urls.each do |url|
86
+ @result_pipe.put({url => @url_checker.check(url)})
87
+ end
88
+ end
89
+
90
+ def validate(urls)
91
+ @logger.debug("[worker #{@id}] Validating #{urls.size} URLs")
92
+ urls.each do |url|
93
+ @result_pipe.put({url => @url_checker.validate(url)})
94
+ end
95
+ end
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,45 @@
1
+ require 'logging'
2
+ require 'ndd/url_checker/abstract_url_checker'
3
+ require 'ndd/url_checker/forked_url_checker'
4
+ require 'ndd/url_checker/status'
5
+ require 'ndd/url_checker/threaded_url_checker'
6
+
7
+ module NDD
8
+ module UrlChecker
9
+
10
+ # Wraps an instance of ThreadedUrlChecker or ForkedUrlChecker
11
+ # depending of the underlying Ruby implementation.
12
+ # @author David DIDIER
13
+ class ParallelUrlChecker < AbstractUrlChecker
14
+
15
+ attr_reader :delegate
16
+
17
+ # Create a new instance.
18
+ # @param [AbstractUrlChecker] delegate_checker defaults to {NDD::UrlChecker::BlockingUrlChecker}.
19
+ # @param [Fixnum] parallelism the number of threads or processes.
20
+ def initialize(delegate_checker=nil, parallelism=10)
21
+ @logger = Logging.logger[self]
22
+
23
+ @logger.debug "Ruby engine is #{RUBY_ENGINE}"
24
+ if RUBY_ENGINE == 'jruby'
25
+ @logger.info 'Creating a threaded URL checker'
26
+ parallel_checker = ThreadedUrlChecker.new(delegate_checker, parallelism)
27
+ else
28
+ @logger.info 'Creating a forked URL checker'
29
+ parallel_checker = ForkedUrlChecker.new(delegate_checker, parallelism)
30
+ end
31
+
32
+ @delegate = parallel_checker
33
+ end
34
+
35
+ def check(*urls)
36
+ @delegate.check(*urls)
37
+ end
38
+
39
+ def validate(*urls)
40
+ @delegate.validate(*urls)
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,104 @@
1
+ module NDD
2
+ module UrlChecker
3
+
4
+ # The result of a URI test.
5
+ # @author David DIDIER
6
+ # @attr_reader code [String] the state
7
+ # @attr_reader error [String|StandardError] the last error if any
8
+ # @attr_reader uris [Array<String>] the list of requested URI
9
+ class Status
10
+
11
+ attr_reader :code
12
+ attr_reader :error
13
+ attr_reader :uris
14
+
15
+ # Create a new NDD::UrlChecker::Status instance in the unknown state.
16
+ # @param uri [String|URI::HTTP] the requested URI.
17
+ def initialize(uri)
18
+ @uris = [uri.to_s]
19
+ @code = :unknown
20
+ end
21
+
22
+ # Returns the first requested URI.
23
+ # @return [String] the first requested URI.
24
+ def uri
25
+ uris.first
26
+ end
27
+
28
+ # Note that the code :unknown is neither valid nor invalid.
29
+ # @return [Boolean] true if valid, false otherwise.
30
+ def valid?
31
+ VALID_CODES.include? @code
32
+ end
33
+
34
+ # Note that the code :unknown is neither valid nor invalid.
35
+ # @return [Boolean] true if invalid, false otherwise.
36
+ def invalid?
37
+ INVALID_CODES.include? @code
38
+ end
39
+
40
+ # @return [Boolean] true if redirected, false otherwise.
41
+ def redirected?
42
+ @code == :redirected
43
+ end
44
+
45
+
46
+ # When the URI is valid without any redirect.
47
+ # @return [NDD::UrlChecker::Status] self.
48
+ def direct
49
+ update_code(:direct, %i(unknown))
50
+ end
51
+
52
+ # When a generic error is raised.
53
+ # @param error [StandardError|String] the generic error.
54
+ # @return [NDD::UrlChecker::Status] self.
55
+ def failed(error)
56
+ @error = error
57
+ update_code(:failed, %i(unknown redirected))
58
+ end
59
+
60
+ # Adds a new URI to the redirected URI list.
61
+ # @param uri [String|URI::HTTP] the redirection URI.
62
+ # @return [NDD::UrlChecker::Status] self.
63
+ def redirected(uri)
64
+ @uris << uri.to_s
65
+ update_code(:redirected, %i(unknown redirected))
66
+ end
67
+
68
+ # When there are too many redirects.
69
+ # @return [NDD::UrlChecker::Status] self.
70
+ def too_many_redirects
71
+ update_code(:too_many_redirects, %i(unknown redirected))
72
+ end
73
+
74
+ # When the host cannot be resolved.
75
+ # @return [NDD::UrlChecker::Status] self.
76
+ def unknown_host
77
+ update_code(:unknown_host, %i(unknown redirected))
78
+ end
79
+
80
+ def to_s
81
+ self.inspect
82
+ end
83
+
84
+
85
+ private
86
+
87
+ VALID_CODES = %i(direct redirected).freeze
88
+ INVALID_CODES = %i(failed too_many_redirects unknown_host).freeze
89
+
90
+ # Updates the code if the transition is valid.
91
+ # @param code [Symbol] the new code.
92
+ # @param valid_source_codes [Array<Symbol>] the codes from which the transition can happen.
93
+ # @return [NDD::UrlChecker::Status] self.
94
+ def update_code(code, valid_source_codes)
95
+ unless valid_source_codes.include?(@code)
96
+ raise "Changing the status code from :#{@code} to :#{code} is forbidden"
97
+ end
98
+ @code = code
99
+ self
100
+ end
101
+
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,36 @@
1
+ require 'logging'
2
+ require 'ndd/url_checker/abstract_url_checker'
3
+ require 'ndd/url_checker/blocking_url_checker'
4
+ require 'ndd/url_checker/status'
5
+
6
+ module NDD
7
+ module UrlChecker
8
+
9
+ # An URL checker using threads to parallelize processing. Does not work on MRI.
10
+ # @author David DIDIER
11
+ class ThreadedUrlChecker < AbstractUrlChecker
12
+
13
+ attr_reader :delegate
14
+
15
+ # Create a new instance.
16
+ # @param [AbstractUrlChecker] delegate_checker defaults to {NDD::UrlChecker::BlockingUrlChecker}.
17
+ # @param [Fixnum] parallelism the number of threads.
18
+ def initialize(delegate_checker=nil, parallelism=10)
19
+ @logger = Logging.logger[self]
20
+ @delegate = delegate_checker || BlockingUrlChecker.new
21
+ @parallelism = parallelism
22
+ end
23
+
24
+ def check(*urls)
25
+ # delegate.check(*urls)
26
+ raise 'TODO'
27
+ end
28
+
29
+ def validate(*urls)
30
+ # delegate.validate(*urls)
31
+ raise 'TODO'
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe NDD::UrlChecker::AbstractUrlChecker do
4
+
5
+ context '#validate' do
6
+ it 'is not implemented' do
7
+ expect { subject.validate('http://www.valid.mock/') }.to raise_error /must be implemented/
8
+ end
9
+ end
10
+
11
+ context '#check' do
12
+ it 'is not implemented' do
13
+ expect { subject.check('http://www.valid.mock/') }.to raise_error /must be implemented/
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe NDD::UrlChecker::BlockingUrlChecker do
4
+
5
+ before(:all) do
6
+ # Logging.logger.root.level = :debug
7
+ end
8
+
9
+ it_behaves_like 'a single URL checker'
10
+ it_behaves_like 'a multiple URL checker'
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe NDD::UrlChecker::ForkedUrlChecker do
4
+
5
+ before(:all) do
6
+ # Logging.logger.root.level = :debug
7
+ end
8
+
9
+ it_behaves_like 'a single URL checker'
10
+ it_behaves_like 'a multiple URL checker', skip_verify = true
11
+
12
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe NDD::UrlChecker::ParallelUrlChecker do
4
+
5
+ before(:all) do
6
+ # Logging.logger.root.level = :debug
7
+ end
8
+
9
+ # ---------------------------------------------------------------------------------------------------------- MRI -----
10
+ context 'when running MRI' do
11
+
12
+ before(:all) do
13
+ @old_engine = RUBY_ENGINE
14
+ silence_warnings { RUBY_ENGINE = 'ruby' }
15
+ end
16
+
17
+ describe '#initialize' do
18
+ it 'delegates to a ForkedUrlChecker instance' do
19
+ expect(NDD::UrlChecker::ParallelUrlChecker.new.delegate).to be_a NDD::UrlChecker::ForkedUrlChecker
20
+ end
21
+ end
22
+
23
+ it_behaves_like 'a single URL checker'
24
+
25
+ after(:all) do
26
+ silence_warnings { RUBY_ENGINE = @old_engine }
27
+ end
28
+ end
29
+
30
+ # -------------------------------------------------------------------------------------------------------- JRuby -----
31
+ context 'when running JRuby' do
32
+ before(:all) do
33
+ @old_engine = RUBY_ENGINE
34
+ silence_warnings { RUBY_ENGINE = 'jruby' }
35
+ end
36
+
37
+ describe '#initialize' do
38
+ it 'delegates to a ThreadedUrlChecker instance' do
39
+ expect(NDD::UrlChecker::ParallelUrlChecker.new.delegate).to be_a NDD::UrlChecker::ThreadedUrlChecker
40
+ end
41
+ end
42
+
43
+ # TODO
44
+ # it_behaves_like 'a single URL checker'
45
+
46
+ after(:all) do
47
+ silence_warnings { RUBY_ENGINE = @old_engine }
48
+ end
49
+ end
50
+
51
+
52
+ # ------------------------------------------------------------------------------------------------------ private -----
53
+ private
54
+
55
+ def silence_warnings(&block)
56
+ warn_level = $VERBOSE
57
+ $VERBOSE = nil
58
+ result = block.call
59
+ $VERBOSE = warn_level
60
+ result
61
+ end
62
+ end
@@ -0,0 +1,510 @@
1
+ require 'spec_helper'
2
+
3
+ describe NDD::UrlChecker::Status do
4
+
5
+ # ------------------------------------------------------------------------------------------------------ unknown -----
6
+ context 'when initialized' do
7
+ let(:uri) { 'http://www.example.com' }
8
+ let(:status) { NDD::UrlChecker::Status.new(uri) }
9
+
10
+ context '#uri' do
11
+ it 'returns the original URI' do
12
+ expect(status.uri).to eq uri
13
+ end
14
+ end
15
+
16
+ context '#uris' do
17
+ it 'returns the original URI' do
18
+ expect(status.uris).to eq [uri]
19
+ end
20
+ end
21
+
22
+ context '#code' do
23
+ it 'returns :unknown' do
24
+ expect(status.code).to eq :unknown
25
+ end
26
+ end
27
+
28
+ context '#valid?' do
29
+ it 'returns false' do
30
+ expect(status.valid?).to be_falsey
31
+ end
32
+ end
33
+
34
+ context '#invalid?' do
35
+ it 'returns false' do
36
+ expect(status.invalid?).to be_falsey
37
+ end
38
+ end
39
+
40
+ context '#error' do
41
+ it 'returns nil' do
42
+ expect(status.error).to be_nil
43
+ end
44
+ end
45
+
46
+ context '#direct' do
47
+ let!(:new_status) { status.direct }
48
+ it 'changes the code to :direct' do
49
+ expect(status.code).to eq :direct
50
+ end
51
+ it 'returns the status' do
52
+ expect(new_status).to eq status
53
+ end
54
+ end
55
+
56
+ context '#failed' do
57
+ let!(:new_status) { status.failed('some error') }
58
+ it 'changes the code to :failed' do
59
+ expect(status.code).to eq :failed
60
+ end
61
+ it 'returns the status' do
62
+ expect(new_status).to eq status
63
+ end
64
+ end
65
+
66
+ context '#redirected' do
67
+ let!(:new_status) { status.redirected('http://www.redirected.com') }
68
+ it 'changes the code to :redirected' do
69
+ expect(status.code).to eq :redirected
70
+ end
71
+ it 'returns the status' do
72
+ expect(new_status).to eq status
73
+ end
74
+ end
75
+
76
+ context '#too_many_redirects' do
77
+ let!(:new_status) { status.too_many_redirects }
78
+ it 'changes the code to :too_many_redirects' do
79
+ expect(status.code).to eq :too_many_redirects
80
+ end
81
+ it 'returns the status' do
82
+ expect(new_status).to eq status
83
+ end
84
+ end
85
+
86
+ context '#unknown_host' do
87
+ it 'changes the code to :unknown_host' do
88
+ status.unknown_host
89
+ expect(status.code).to eq :unknown_host
90
+ end
91
+ it 'returns the status' do
92
+ expect(status.unknown_host).to eq status
93
+ end
94
+ end
95
+
96
+ context '#to_s' do
97
+ it 'returns the status representation' do
98
+ expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:unknown>$}
99
+ end
100
+ end
101
+ end
102
+
103
+ # ------------------------------------------------------------------------------------------------------- direct -----
104
+ context 'when code is :direct' do
105
+ let(:uri) { 'http://www.example.com' }
106
+ let(:status) { NDD::UrlChecker::Status.new(uri).direct }
107
+
108
+ context '#uri' do
109
+ it 'returns the original URI' do
110
+ expect(status.uri).to eq uri
111
+ end
112
+ end
113
+
114
+ context '#uris' do
115
+ it 'returns the original URI' do
116
+ expect(status.uris).to eq [uri]
117
+ end
118
+ end
119
+
120
+ context '#code' do
121
+ it 'returns :direct' do
122
+ expect(status.code).to eq :direct
123
+ end
124
+ end
125
+
126
+ context '#valid?' do
127
+ it 'returns true' do
128
+ expect(status.valid?).to be_truthy
129
+ end
130
+ end
131
+
132
+ context '#invalid?' do
133
+ it 'returns false' do
134
+ expect(status.invalid?).to be_falsey
135
+ end
136
+ end
137
+
138
+ context '#error' do
139
+ it 'returns nil' do
140
+ expect(status.error).to be_nil
141
+ end
142
+ end
143
+
144
+ context '#direct' do
145
+ it 'raises an error' do
146
+ expect { status.direct }.to raise_error(/from :direct to :direct is forbidden/)
147
+ end
148
+ end
149
+
150
+ context '#failed' do
151
+ it 'raises an error' do
152
+ expect { status.failed('some error') }.to raise_error(/from :direct to :failed is forbidden/)
153
+ end
154
+ end
155
+
156
+ context '#redirected' do
157
+ it 'raises an error' do
158
+ expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :direct to :redirected is forbidden/)
159
+ end
160
+ end
161
+
162
+ context '#too_many_redirects' do
163
+ it 'raises an error' do
164
+ expect { status.too_many_redirects }.to raise_error(/from :direct to :too_many_redirects is forbidden/)
165
+ end
166
+ end
167
+
168
+ context '#unknown_host' do
169
+ it 'raises an error' do
170
+ expect { status.unknown_host }.to raise_error(/from :direct to :unknown_host is forbidden/)
171
+ end
172
+ end
173
+
174
+ context '#to_s' do
175
+ it 'returns the status representation' do
176
+ expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:direct>$}
177
+ end
178
+ end
179
+ end
180
+
181
+ # ------------------------------------------------------------------------------------------------------- failed -----
182
+ context 'when code is :failed' do
183
+ let(:uri) { 'http://www.example.com' }
184
+ let(:status) { NDD::UrlChecker::Status.new(uri).failed('some error') }
185
+
186
+ context '#uri' do
187
+ it 'returns the original URI' do
188
+ expect(status.uri).to eq uri
189
+ end
190
+ end
191
+
192
+ context '#uris' do
193
+ it 'returns the original URI' do
194
+ expect(status.uris).to eq [uri]
195
+ end
196
+ end
197
+
198
+ context '#code' do
199
+ it 'returns :failed' do
200
+ expect(status.code).to eq :failed
201
+ end
202
+ end
203
+
204
+ context '#valid?' do
205
+ it 'returns false' do
206
+ expect(status.valid?).to be_falsey
207
+ end
208
+ end
209
+
210
+ context '#invalid?' do
211
+ it 'returns true' do
212
+ expect(status.invalid?).to be_truthy
213
+ end
214
+ end
215
+
216
+ context '#error' do
217
+ it 'returns the error' do
218
+ expect(status.error).to eq 'some error'
219
+ end
220
+ end
221
+
222
+ context '#direct' do
223
+ it 'raises an error' do
224
+ expect { status.direct }.to raise_error(/from :failed to :direct is forbidden/)
225
+ end
226
+ end
227
+
228
+ context '#failed' do
229
+ it 'raises an error' do
230
+ expect { status.failed('some error') }.to raise_error(/from :failed to :failed is forbidden/)
231
+ end
232
+ end
233
+
234
+ context '#redirected' do
235
+ it 'raises an error' do
236
+ expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :failed to :redirected is forbidden/)
237
+ end
238
+ end
239
+
240
+ context '#too_many_redirects' do
241
+ it 'raises an error' do
242
+ expect { status.too_many_redirects }.to raise_error(/from :failed to :too_many_redirects is forbidden/)
243
+ end
244
+ end
245
+
246
+ context '#unknown_host' do
247
+ it 'raises an error' do
248
+ expect { status.unknown_host }.to raise_error(/from :failed to :unknown_host is forbidden/)
249
+ end
250
+ end
251
+
252
+ context '#to_s' do
253
+ it 'returns the status representation' do
254
+ expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:failed, @error="some error">$}
255
+ end
256
+ end
257
+ end
258
+
259
+ # --------------------------------------------------------------------------------------------------- redirected -----
260
+ context 'when code is :redirected' do
261
+ let(:uri) { 'http://www.example.com' }
262
+ let(:redirect_uri) { 'http://www.redirected.com' }
263
+ let(:status) { NDD::UrlChecker::Status.new(uri).redirected(redirect_uri) }
264
+
265
+ context '#uri' do
266
+ it 'returns the original URI' do
267
+ expect(status.uri).to eq uri
268
+ end
269
+ end
270
+
271
+ context '#uris' do
272
+ it 'returns all the URI' do
273
+ expect(status.uris).to eq [uri, redirect_uri]
274
+ end
275
+ end
276
+
277
+ context '#code' do
278
+ it 'returns :redirected' do
279
+ expect(status.code).to eq :redirected
280
+ end
281
+ end
282
+
283
+ context '#valid?' do
284
+ it 'returns true' do
285
+ expect(status.valid?).to be_truthy
286
+ end
287
+ end
288
+
289
+ context '#invalid?' do
290
+ it 'returns false' do
291
+ expect(status.invalid?).to be_falsey
292
+ end
293
+ end
294
+
295
+ context '#error' do
296
+ it 'returns nil' do
297
+ expect(status.error).to be_nil
298
+ end
299
+ end
300
+
301
+ context '#direct' do
302
+ it 'raises an error' do
303
+ expect { status.direct }.to raise_error(/from :redirected to :direct is forbidden/)
304
+ end
305
+ end
306
+
307
+ context '#failed' do
308
+ let!(:new_status) { status.failed('some error') }
309
+ it 'changes the code to :failed' do
310
+ expect(status.code).to eq :failed
311
+ end
312
+ it 'returns the status' do
313
+ expect(new_status).to eq status
314
+ end
315
+ end
316
+
317
+ context '#redirected' do
318
+ let!(:new_status) { status.redirected('http://www.redirected.com') }
319
+ it 'changes the code to :redirected' do
320
+ expect(status.code).to eq :redirected
321
+ end
322
+ it 'returns the status' do
323
+ expect(new_status).to eq status
324
+ end
325
+ end
326
+
327
+ context '#too_many_redirects' do
328
+ let!(:new_status) { status.too_many_redirects }
329
+ it 'changes the code to :too_many_redirects' do
330
+ expect(status.code).to eq :too_many_redirects
331
+ end
332
+ it 'returns the status' do
333
+ expect(new_status).to eq status
334
+ end
335
+ end
336
+
337
+ context '#unknown_host' do
338
+ it 'changes the code to :unknown_host' do
339
+ status.unknown_host
340
+ expect(status.code).to eq :unknown_host
341
+ end
342
+ it 'returns the status' do
343
+ expect(status.unknown_host).to eq status
344
+ end
345
+ end
346
+
347
+ context '#to_s' do
348
+ it 'returns the status representation' do
349
+ expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com", "http://www.redirected.com"\], @code=:redirected>$}
350
+ end
351
+ end
352
+ end
353
+
354
+ # ------------------------------------------------------------------------------------------- too_many_redirects -----
355
+ context 'when code is :too_many_redirects' do
356
+ let(:uri) { 'http://www.example.com' }
357
+ let(:status) { NDD::UrlChecker::Status.new(uri).too_many_redirects }
358
+
359
+ context '#uri' do
360
+ it 'returns the original URI' do
361
+ expect(status.uri).to eq uri
362
+ end
363
+ end
364
+
365
+ context '#uris' do
366
+ it 'returns the original URI' do
367
+ expect(status.uris).to eq [uri]
368
+ end
369
+ end
370
+
371
+ context '#code' do
372
+ it 'returns :too_many_redirects' do
373
+ expect(status.code).to eq :too_many_redirects
374
+ end
375
+ end
376
+
377
+ context '#valid?' do
378
+ it 'returns false' do
379
+ expect(status.valid?).to be_falsey
380
+ end
381
+ end
382
+
383
+ context '#invalid?' do
384
+ it 'returns true' do
385
+ expect(status.invalid?).to be_truthy
386
+ end
387
+ end
388
+
389
+ context '#error' do
390
+ it 'returns nil' do
391
+ expect(status.error).to be_nil
392
+ end
393
+ end
394
+
395
+ context '#direct' do
396
+ it 'raises an error' do
397
+ expect { status.direct }.to raise_error(/from :too_many_redirects to :direct is forbidden/)
398
+ end
399
+ end
400
+
401
+ context '#failed' do
402
+ it 'raises an error' do
403
+ expect { status.failed('some error') }.to raise_error(/from :too_many_redirects to :failed is forbidden/)
404
+ end
405
+ end
406
+
407
+ context '#redirected' do
408
+ it 'raises an error' do
409
+ expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :too_many_redirects to :redirected is forbidden/)
410
+ end
411
+ end
412
+
413
+ context '#too_many_redirects' do
414
+ it 'raises an error' do
415
+ expect { status.too_many_redirects }.to raise_error(/from :too_many_redirects to :too_many_redirects is forbidden/)
416
+ end
417
+ end
418
+
419
+ context '#unknown_host' do
420
+ it 'raises an error' do
421
+ expect { status.unknown_host }.to raise_error(/from :too_many_redirects to :unknown_host is forbidden/)
422
+ end
423
+ end
424
+
425
+ context '#to_s' do
426
+ it 'returns the status representation' do
427
+ expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:too_many_redirects>$}
428
+ end
429
+ end
430
+ end
431
+
432
+ # ------------------------------------------------------------------------------------------------- unknown_host -----
433
+ context 'when code is :unknown_host' do
434
+ let(:uri) { 'http://www.example.com' }
435
+ let(:status) { NDD::UrlChecker::Status.new(uri).unknown_host }
436
+
437
+ context '#uri' do
438
+ it 'returns the original URI' do
439
+ expect(status.uri).to eq uri
440
+ end
441
+ end
442
+
443
+ context '#uris' do
444
+ it 'returns the original URI' do
445
+ expect(status.uris).to eq [uri]
446
+ end
447
+ end
448
+
449
+ context '#code' do
450
+ it 'returns :unknown_host' do
451
+ expect(status.code).to eq :unknown_host
452
+ end
453
+ end
454
+
455
+ context '#valid?' do
456
+ it 'returns false' do
457
+ expect(status.valid?).to be_falsey
458
+ end
459
+ end
460
+
461
+ context '#invalid?' do
462
+ it 'returns true' do
463
+ expect(status.invalid?).to be_truthy
464
+ end
465
+ end
466
+
467
+ context '#error' do
468
+ it 'returns nil' do
469
+ expect(status.error).to be_nil
470
+ end
471
+ end
472
+
473
+ context '#direct' do
474
+ it 'raises an error' do
475
+ expect { status.direct }.to raise_error(/from :unknown_host to :direct is forbidden/)
476
+ end
477
+ end
478
+
479
+ context '#failed' do
480
+ it 'raises an error' do
481
+ expect { status.failed('some error') }.to raise_error(/from :unknown_host to :failed is forbidden/)
482
+ end
483
+ end
484
+
485
+ context '#redirected' do
486
+ it 'raises an error' do
487
+ expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :unknown_host to :redirected is forbidden/)
488
+ end
489
+ end
490
+
491
+ context '#too_many_redirects' do
492
+ it 'raises an error' do
493
+ expect { status.too_many_redirects }.to raise_error(/from :unknown_host to :too_many_redirects is forbidden/)
494
+ end
495
+ end
496
+
497
+ context '#unknown_host' do
498
+ it 'raises an error' do
499
+ expect { status.unknown_host }.to raise_error(/from :unknown_host to :unknown_host is forbidden/)
500
+ end
501
+ end
502
+
503
+ context '#to_s' do
504
+ it 'returns the status representation' do
505
+ expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:unknown_host>$}
506
+ end
507
+ end
508
+ end
509
+
510
+ end