faraday_cage 0.0.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,32 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'rack'
4
+
5
+ require 'faraday_cage/rack_middleware'
6
+
7
+ module FaradayCage
8
+ class Server
9
+
10
+ attr_reader :app
11
+
12
+ def initialize(app = FaradayCage.app)
13
+ if app
14
+ @app = app
15
+ else
16
+ raise Error, 'No Rack app configured. Please see documentation for details.'
17
+ end
18
+
19
+ @middleware = RackMiddleware.new(app)
20
+ end
21
+
22
+ def connection
23
+ @connection ||= Faraday.new(FaradayCage.default_host) do |conn|
24
+ if FaradayCage.middleware.respond_to?(:call)
25
+ FaradayCage.middleware.call(conn)
26
+ end
27
+
28
+ conn.adapter :rack, app
29
+ end
30
+ end
31
+ end # Server
32
+ end # FaradayCage
@@ -0,0 +1,164 @@
1
+ module FaradayCage
2
+ ##
3
+ # Wraps status codes to provide various helper methods for testing a response.
4
+ #
5
+ # @example Testing whether a status is successful.
6
+ #
7
+ # status.success?
8
+ #
9
+ # @example Getting a status name.
10
+ #
11
+ # status.name
12
+ # # => :service_unavailable
13
+ #
14
+ # @example Comparing a status.
15
+ #
16
+ # status == 200
17
+ # # => true
18
+ #
19
+ # @example Converting a status.
20
+ #
21
+ # status.to_i
22
+ # # => 422
23
+ # status.to_sym
24
+ # # => :unprocessable_entity
25
+ #
26
+ # @example Testing a status by name.
27
+ #
28
+ # status.forbidden?
29
+ # # => false
30
+ class Status
31
+ MAPPINGS = {
32
+ 100 => :continue,
33
+ 101 => :switching_protocols,
34
+ 102 => :processing,
35
+ 200 => :ok,
36
+ 201 => :created,
37
+ 202 => :accepted,
38
+ 203 => :non_authoritative_information,
39
+ 204 => :no_content,
40
+ 205 => :reset_content,
41
+ 206 => :partial_content,
42
+ 207 => :multi_status,
43
+ 226 => :im_used,
44
+ 300 => :multiple_choices,
45
+ 301 => :moved_permanently,
46
+ 302 => :found,
47
+ 303 => :see_other,
48
+ 304 => :not_modified,
49
+ 305 => :use_proxy,
50
+ 307 => :temporary_redirect,
51
+ 400 => :bad_request,
52
+ 401 => :unauthorized,
53
+ 402 => :payment_required,
54
+ 403 => :forbidden,
55
+ 404 => :not_found,
56
+ 405 => :method_not_allowed,
57
+ 406 => :not_acceptable,
58
+ 407 => :proxy_authentication_required,
59
+ 408 => :request_timeout,
60
+ 409 => :conflict,
61
+ 410 => :gone,
62
+ 411 => :length_required,
63
+ 412 => :precondition_failed,
64
+ 413 => :request_entity_too_large,
65
+ 414 => :request_uri_too_long,
66
+ 415 => :unsupported_media_type,
67
+ 416 => :requested_range_not_satisfiable,
68
+ 417 => :expectation_failed,
69
+ 422 => :unprocessable_entity,
70
+ 423 => :locked,
71
+ 424 => :failed_dependency,
72
+ 426 => :upgrade_required,
73
+ 500 => :internal_server_error,
74
+ 501 => :not_implemented,
75
+ 502 => :bad_gateway,
76
+ 503 => :service_unavailable,
77
+ 504 => :gateway_timeout,
78
+ 505 => :http_version_not_supported,
79
+ 507 => :insufficient_storage,
80
+ 510 => :not_extended
81
+ }
82
+
83
+ MAPPINGS.each do |code, method|
84
+ class_eval <<-RUBY
85
+ def #{method}?
86
+ code == #{code}
87
+ end
88
+ RUBY
89
+ end
90
+
91
+ attr_reader :code
92
+
93
+ def initialize(code)
94
+ @code = code.to_i
95
+ end
96
+
97
+ def ==(object)
98
+ if object.respond_to?(:code)
99
+ object.code == code
100
+ else
101
+ object == code || object == name
102
+ end
103
+ end
104
+
105
+ def name
106
+ @name ||= MAPPINGS[code]
107
+ end
108
+
109
+ def to_i
110
+ code
111
+ end
112
+
113
+ def to_s
114
+ name.to_s
115
+ end
116
+
117
+ def to_sym
118
+ name
119
+ end
120
+
121
+ def inspect
122
+ "#<#{self.class} code: #{code.inspect} name: #{name.inspect}>"
123
+ end
124
+
125
+ def type
126
+ @type ||= case code
127
+ when 100..199 then :informational
128
+ when 200..299 then :success
129
+ when 300..399 then :redirect
130
+ when 400..499 then :client_error
131
+ when 500..599 then :server_error
132
+ else :unknown
133
+ end
134
+ end
135
+
136
+ def informational?
137
+ type == :informational
138
+ end
139
+
140
+ def success?
141
+ type == :success
142
+ end
143
+
144
+ def redirect?
145
+ type == :redirect
146
+ end
147
+
148
+ def client_error?
149
+ type == :client_error
150
+ end
151
+
152
+ def server_error?
153
+ type == :server_error
154
+ end
155
+
156
+ def error?
157
+ client_error? || server_error?
158
+ end
159
+
160
+ def unknown?
161
+ type == :unknown
162
+ end
163
+ end # Status
164
+ end # FaradayCage
@@ -0,0 +1,3 @@
1
+ module FaradayCage
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe FaradayCage::Config do
4
+
5
+ describe '#initialize' do
6
+
7
+ it 'sets the default host' do
8
+ expect(described_class.new.default_host).to eq('http://example.com')
9
+ end
10
+ end
11
+
12
+ describe '#app' do
13
+
14
+ let(:app) do
15
+ nil
16
+ end
17
+
18
+ subject(:config) do
19
+ described_class.new.tap do |config|
20
+ config.app = app
21
+ end
22
+ end
23
+
24
+ context 'when an app is set' do
25
+
26
+ let(:app) do
27
+ :foo
28
+ end
29
+
30
+ it 'returns the set app' do
31
+ expect(config.app).to eq(:foo)
32
+ end
33
+ end
34
+
35
+ context 'when an app is not set' do
36
+
37
+ context 'when Rails is defined' do
38
+
39
+ it 'returns #default_rails_app' do
40
+ stub_const('Rails', Class.new)
41
+ config.should_receive(:default_rails_app).and_return(:bar)
42
+ expect(config.app).to eq(:bar)
43
+ end
44
+ end
45
+
46
+ context 'when Rails is not defined' do
47
+
48
+ it 'returns nil' do
49
+ expect(config.app).to be_nil
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#middleware' do
56
+
57
+ subject(:config) do
58
+ described_class.new
59
+ end
60
+
61
+ context 'when a block is given' do
62
+
63
+ it 'stores the provided block as a proc' do
64
+ block = -> { 'blah blah blah' }
65
+ config.middleware(&block)
66
+ expect(config.middleware).to eq(block)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe FaradayCage::RackMiddleware do
4
+
5
+ describe '#initialize' do
6
+
7
+ it 'initializes with the provided app' do
8
+ expect(described_class.new(:foo).app).to eq(:foo)
9
+ end
10
+ end
11
+
12
+ describe '#call' do
13
+
14
+ let(:app) do
15
+ ->(env) { [123, { foo: 'bar' }, ['it works!']] }
16
+ end
17
+
18
+ subject(:middleware) do
19
+ described_class.new(app)
20
+ end
21
+
22
+ context 'when request is for /__identify__' do
23
+
24
+ let(:env) do
25
+ { 'PATH_INFO' => '/__identify__' }
26
+ end
27
+
28
+ it 'has a 200 status code' do
29
+ expect(middleware.call(env)[0]).to eq(200)
30
+ end
31
+
32
+ it 'has a content type of text/plain' do
33
+ expect(middleware.call(env)[1]).to eq({ 'Content-Type' => 'text/plain' })
34
+ end
35
+
36
+ it 'returns the object ID of the app' do
37
+ expect(middleware.call(env)[2]).to eq([middleware.app.object_id.to_s])
38
+ end
39
+ end
40
+
41
+ context 'when request is not for /__identify__' do
42
+
43
+ let(:env) do
44
+ { 'PATH_INFO' => '/something_else' }
45
+ end
46
+
47
+ it 'calls the app and returns the response' do
48
+ expect(middleware.call(env)).to eq([123, { foo: 'bar' }, ['it works!']])
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe FaradayCage::Server do
4
+
5
+ describe '#initialize' do
6
+
7
+ let(:app) do
8
+ double('Rack app')
9
+ end
10
+
11
+ it 'wraps the provided app in RackMiddleware' do
12
+ expect(described_class.new(app).app).to eq(app)
13
+ end
14
+
15
+ context 'when there is no default app configured' do
16
+
17
+ it 'raises an error' do
18
+ expect { described_class.new }.to raise_error(FaradayCage::Error)
19
+ end
20
+ end
21
+ end
22
+
23
+ describe '#connection' do
24
+
25
+ subject(:server) do
26
+ described_class.new(double('Rack app'))
27
+ end
28
+
29
+ it 'returns a Faraday::Connection' do
30
+ expect(server.connection).to be_a(Faraday::Connection)
31
+ end
32
+
33
+ it 'sets the URL to FaradayCage.default_host' do
34
+ expect(server.connection.url_prefix).to eq(URI.parse(FaradayCage.default_host))
35
+ end
36
+
37
+ it 'uses the Rack adapter' do
38
+ expect(server.connection.builder.handlers.first).to eq(Faraday::Adapter::Rack)
39
+ end
40
+
41
+ context 'when FaradayCage.middleware is set' do
42
+
43
+ let(:middleware) do
44
+ double('middleware')
45
+ end
46
+
47
+ it 'calls the stored proc with the connection' do
48
+ FaradayCage.should_receive(:middleware).at_least(1).times.and_return(middleware)
49
+ middleware.should_receive(:respond_to?).with(:call).and_return(true)
50
+ middleware.should_receive(:call) { |conn| expect(conn).to be_a(Faraday::Connection) }
51
+ server.connection
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,375 @@
1
+ require 'spec_helper'
2
+
3
+ describe FaradayCage::Status do
4
+
5
+ describe '#initialize' do
6
+
7
+ it 'initializes with a status code' do
8
+ described_class.new(200)
9
+ end
10
+
11
+ it 'converts the status code to an integer' do
12
+ expect(described_class.new('500').code).to eq(500)
13
+ end
14
+ end
15
+
16
+ FaradayCage::Status::MAPPINGS.each do |code, mapping|
17
+
18
+ describe "##{mapping}?" do
19
+
20
+ it "returns true when #code equals #{code}" do
21
+ expect(described_class.new(code).send(:"#{mapping}?")).to be_true
22
+ end
23
+
24
+ it "returns false when #code does not equal #{code}" do
25
+ expect(described_class.new(0).send(:"#{mapping}?")).to be_false
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#name' do
31
+
32
+ it 'returns the mapping for the code' do
33
+ FaradayCage::Status::MAPPINGS.should_receive(:[]).with(5678).and_return(:foo)
34
+ expect(described_class.new(5678).name).to eq(:foo)
35
+ end
36
+
37
+ it 'caches the mapping' do
38
+ status = described_class.new(200)
39
+ expect(status.name.object_id).to eq(status.name.object_id)
40
+ end
41
+
42
+ context 'when there is no mapping for the code' do
43
+
44
+ it 'returns nil' do
45
+ expect(described_class.new(0).name).to be_nil
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#==' do
51
+
52
+ subject(:status) do
53
+ described_class.new(404)
54
+ end
55
+
56
+ context 'when provided object responds to #code' do
57
+
58
+ context 'when codes match' do
59
+
60
+ it 'returns true' do
61
+ expect(status == double('object', code: 404)).to be_true
62
+ end
63
+ end
64
+
65
+ context 'when codes do not match' do
66
+
67
+ it 'returns false' do
68
+ expect(status == double('object', code: 444)).to be_false
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'when provided object does not respond to #code' do
74
+
75
+ context 'when object matches #code' do
76
+
77
+ it 'returns true' do
78
+ expect(status == 404).to be_true
79
+ end
80
+ end
81
+
82
+ context 'when object matches #name' do
83
+
84
+ it 'returns true' do
85
+ expect(status == :not_found).to be_true
86
+ end
87
+ end
88
+
89
+ context 'when object matches neither #code nor #name' do
90
+
91
+ it 'returns false' do
92
+ expect(status == nil).to be_false
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ describe '#to_i' do
99
+
100
+ it 'returns the code' do
101
+ status = described_class.new(42)
102
+ status.should_receive(:code).and_return(12)
103
+ expect(status.to_i).to eq(12)
104
+ end
105
+ end
106
+
107
+ describe '#to_s' do
108
+
109
+ it 'returns the name as a string' do
110
+ status = described_class.new(420)
111
+ status.should_receive(:name).and_return(:enhance_your_cool)
112
+ expect(status.to_s).to eq('enhance_your_cool')
113
+ end
114
+ end
115
+
116
+ describe '#to_sym' do
117
+
118
+ it 'returns the name' do
119
+ status = described_class.new(100)
120
+ status.should_receive(:name).and_return(:bar)
121
+ expect(status.to_sym).to eq(:bar)
122
+ end
123
+ end
124
+
125
+ describe '#inspect' do
126
+
127
+ subject(:inspection) do
128
+ described_class.new(200).inspect
129
+ end
130
+
131
+ it 'returns a string' do
132
+ expect(inspection).to be_a(String)
133
+ end
134
+
135
+ it 'includes the class name' do
136
+ expect(inspection).to include('#<FaradayCage::Status')
137
+ end
138
+
139
+ it 'includes the status code' do
140
+ expect(inspection).to include('code: 200')
141
+ end
142
+
143
+ it 'includes the status name' do
144
+ expect(inspection).to include('name: :ok')
145
+ end
146
+ end
147
+
148
+ describe '#type' do
149
+
150
+ context 'when #code is between 100 and 199' do
151
+ it 'returns :informational' do
152
+ expect(described_class.new(99).type).to_not eq(:informational)
153
+
154
+ (100..199).each do |code|
155
+ expect(described_class.new(code).type).to eq(:informational)
156
+ end
157
+
158
+ expect(described_class.new(200).type).to_not eq(:informational)
159
+ end
160
+ end
161
+
162
+ context 'when #code is between 200 and 299' do
163
+
164
+ it 'returns :success' do
165
+ expect(described_class.new(199).type).to_not eq(:success)
166
+
167
+ (200..299).each do |code|
168
+ expect(described_class.new(code).type).to eq(:success)
169
+ end
170
+
171
+ expect(described_class.new(300).type).to_not eq(:success)
172
+ end
173
+ end
174
+
175
+ context 'when #code is between 300 and 399' do
176
+
177
+ it 'returns :redirect' do
178
+ expect(described_class.new(299).type).to_not eq(:redirect)
179
+
180
+ (300..399).each do |code|
181
+ expect(described_class.new(code).type).to eq(:redirect)
182
+ end
183
+
184
+ expect(described_class.new(400).type).to_not eq(:redirect)
185
+ end
186
+ end
187
+
188
+ context 'when #code is between 400 and 499' do
189
+
190
+ it 'returns :client_error' do
191
+ expect(described_class.new(399).type).to_not eq(:client_error)
192
+
193
+ (400..499).each do |code|
194
+ expect(described_class.new(code).type).to eq(:client_error)
195
+ end
196
+
197
+ expect(described_class.new(500).type).to_not eq(:client_error)
198
+ end
199
+ end
200
+
201
+ context 'when #code is between 500 and 599' do
202
+
203
+ it 'returns :redirect' do
204
+ expect(described_class.new(499).type).to_not eq(:server_error)
205
+
206
+ (500..599).each do |code|
207
+ expect(described_class.new(code).type).to eq(:server_error)
208
+ end
209
+
210
+ expect(described_class.new(600).type).to_not eq(:server_error)
211
+ end
212
+ end
213
+
214
+ context 'when #code is not between 100 and 599' do
215
+
216
+ it 'returns :unknown' do
217
+ (0..99).each do |code|
218
+ expect(described_class.new(code).type).to eq(:unknown)
219
+ end
220
+
221
+ (600..1000).each do |code|
222
+ expect(described_class.new(code).type).to eq(:unknown)
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ describe 'status type matchers' do
229
+
230
+ subject(:status) do
231
+ described_class.new(rand(0..1000))
232
+ end
233
+
234
+ describe '#informational?' do
235
+
236
+ context 'when #type is :informational' do
237
+
238
+ it 'returns true' do
239
+ status.should_receive(:type).and_return(:informational)
240
+ expect(status.informational?).to be_true
241
+ end
242
+ end
243
+
244
+ context 'when #type is not :informational' do
245
+
246
+ it 'returns false' do
247
+ status.should_receive(:type).and_return(:dreadful)
248
+ expect(status.informational?).to be_false
249
+ end
250
+ end
251
+ end
252
+
253
+ describe '#success?' do
254
+
255
+ context 'when #type is :success' do
256
+
257
+ it 'returns true' do
258
+ status.should_receive(:type).and_return(:success)
259
+ expect(status.success?).to be_true
260
+ end
261
+ end
262
+
263
+ context 'when #type is not :success' do
264
+
265
+ it 'returns false' do
266
+ status.should_receive(:type).and_return(:failure)
267
+ expect(status.success?).to be_false
268
+ end
269
+ end
270
+ end
271
+
272
+ describe '#redirect?' do
273
+
274
+ describe 'when #type is :redirect' do
275
+
276
+ it 'returns true' do
277
+ status.should_receive(:type).and_return(:redirect)
278
+ expect(status.redirect?).to be_true
279
+ end
280
+ end
281
+
282
+ describe 'when #type is not redirect' do
283
+
284
+ it 'returns false' do
285
+ status.should_receive(:type).and_return(:stay_here)
286
+ expect(status.redirect?).to be_false
287
+ end
288
+ end
289
+ end
290
+
291
+ describe '#client_error?' do
292
+
293
+ context 'when #type is :client_error' do
294
+
295
+ it 'returns true' do
296
+ status.should_receive(:type).at_least(1).times.and_return(:client_error)
297
+ expect(status.client_error?).to be_true
298
+ end
299
+ end
300
+
301
+ context 'when #type is not :client_error' do
302
+
303
+ it 'returns false' do
304
+ status.should_receive(:type).at_least(1).times.and_return(:not_an_error)
305
+ expect(status.client_error?).to be_false
306
+ end
307
+ end
308
+ end
309
+
310
+ describe '#server_error?' do
311
+
312
+ context 'when #type is :server_error' do
313
+
314
+ it 'returns true' do
315
+ status.should_receive(:type).and_return(:server_error)
316
+ expect(status.server_error?).to be_true
317
+ end
318
+ end
319
+
320
+ context 'when #type is not :server_error' do
321
+
322
+ it 'returns false' do
323
+ status.should_receive(:type).and_return(:useless)
324
+ expect(status.server_error?).to be_false
325
+ end
326
+ end
327
+ end
328
+
329
+ describe '#error?' do
330
+
331
+ context 'when #type is :client_error' do
332
+
333
+ it 'returns true' do
334
+ status.should_receive(:type).at_least(1).times.and_return(:client_error)
335
+ expect(status.error?).to be_true
336
+ end
337
+ end
338
+
339
+ context 'when #type is :server_error' do
340
+
341
+ it 'returns true' do
342
+ status.should_receive(:type).at_least(1).times.and_return(:server_error)
343
+ expect(status.error?).to be_true
344
+ end
345
+ end
346
+
347
+ context 'when #type is neither :client_error nor :server_error' do
348
+
349
+ it 'returns false' do
350
+ status.should_receive(:type).at_least(1).times.and_return(:operator_error)
351
+ expect(status.error?).to be_false
352
+ end
353
+ end
354
+ end
355
+
356
+ describe '#unknown?' do
357
+
358
+ context 'when #type is :unknown' do
359
+
360
+ it 'returns true' do
361
+ status.should_receive(:type).and_return(:unknown)
362
+ expect(status.unknown?).to be_true
363
+ end
364
+ end
365
+
366
+ context 'when #type is not unknown' do
367
+
368
+ it 'returns false' do
369
+ status.should_receive(:type).and_return(:known)
370
+ expect(status.unknown?).to be_false
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end