response 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.travis.yml +16 -0
- data/Changelog.md +3 -0
- data/Gemfile +6 -0
- data/Gemfile.devtools +60 -0
- data/Guardfile +18 -0
- data/LICENSE +20 -0
- data/README.md +39 -0
- data/Rakefile +2 -0
- data/TODO +2 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +92 -0
- data/config/roodi.yml +18 -0
- data/config/yardstick.yml +2 -0
- data/lib/response/html.rb +36 -0
- data/lib/response/json.rb +36 -0
- data/lib/response/redirect.rb +52 -0
- data/lib/response/status.rb +28 -0
- data/lib/response/text.rb +37 -0
- data/lib/response/xml.rb +36 -0
- data/lib/response.rb +271 -0
- data/response.gemspec +22 -0
- data/spec/rcov.opts +7 -0
- data/spec/shared/functional_command_method_behavior.rb +11 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/response/body_spec.rb +22 -0
- data/spec/unit/response/cache_control_spec.rb +25 -0
- data/spec/unit/response/class_methods/build_spec.rb +31 -0
- data/spec/unit/response/content_type_spec.rb +26 -0
- data/spec/unit/response/headers_spec.rb +23 -0
- data/spec/unit/response/html/class_methods/build_spec.rb +18 -0
- data/spec/unit/response/json/class_methods/build_spec.rb +18 -0
- data/spec/unit/response/last_modified_spec.rb +25 -0
- data/spec/unit/response/merge_headers_spec.rb +20 -0
- data/spec/unit/response/rack_array_spec.rb +15 -0
- data/spec/unit/response/redirect/class_methods/build_spec.rb +28 -0
- data/spec/unit/response/status_spec.rb +22 -0
- data/spec/unit/response/text/class_methods/build_spec.rb +18 -0
- data/spec/unit/response/to_rack_response_spec.rb +26 -0
- data/spec/unit/response/valid_predicate_spec.rb +37 -0
- data/spec/unit/response/with_body_spec.rb +17 -0
- data/spec/unit/response/with_headers_spec.rb +17 -0
- data/spec/unit/response/with_status_spec.rb +17 -0
- data/spec/unit/response/xml/class_methods/build_spec.rb +18 -0
- data/tasks/metrics/ci.rake +7 -0
- data/tasks/metrics/flay.rake +47 -0
- data/tasks/metrics/flog.rake +43 -0
- data/tasks/metrics/heckle.rake +208 -0
- data/tasks/metrics/metric_fu.rake +29 -0
- data/tasks/metrics/reek.rake +15 -0
- data/tasks/metrics/roodi.rake +15 -0
- data/tasks/metrics/yardstick.rake +23 -0
- data/tasks/spec.rake +45 -0
- data/tasks/yard.rake +9 -0
- metadata +187 -0
data/lib/response.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'time'
|
3
|
+
require 'ice_nine'
|
4
|
+
require 'adamantium'
|
5
|
+
require 'concord'
|
6
|
+
require 'abstract_type'
|
7
|
+
require 'equalizer'
|
8
|
+
|
9
|
+
# Library to build rack compatible responses in a functional style
|
10
|
+
class Response
|
11
|
+
include Adamantium::Flat, Concord.new(:status, :headers, :body)
|
12
|
+
|
13
|
+
# Error raised when finalizing responses with undefined components
|
14
|
+
class InvalidError < RuntimeError; end
|
15
|
+
|
16
|
+
TEXT_PLAIN = 'text/plain; charset=UTF-8'.freeze
|
17
|
+
|
18
|
+
# Undefined response component
|
19
|
+
#
|
20
|
+
# A class to get nice #inspect behavior ootb
|
21
|
+
#
|
22
|
+
Undefined = Class.new.freeze
|
23
|
+
|
24
|
+
# Return status code
|
25
|
+
#
|
26
|
+
# @return [Response::Status]
|
27
|
+
# if set
|
28
|
+
#
|
29
|
+
# @return [undefined]
|
30
|
+
# otherwise
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
attr_reader :status
|
35
|
+
|
36
|
+
# Return headers code
|
37
|
+
#
|
38
|
+
# @return [Hash]
|
39
|
+
# if set
|
40
|
+
#
|
41
|
+
# @return [undefined]
|
42
|
+
# otherwise
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
#
|
46
|
+
attr_reader :headers
|
47
|
+
|
48
|
+
# Return status code
|
49
|
+
#
|
50
|
+
# @return [#each]
|
51
|
+
# if set
|
52
|
+
#
|
53
|
+
# @return [undefined]
|
54
|
+
# otherwise
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
attr_reader :body
|
59
|
+
|
60
|
+
# Return response with new status
|
61
|
+
#
|
62
|
+
# @param [Fixnum] status
|
63
|
+
# the status for the response
|
64
|
+
#
|
65
|
+
# @return [Response]
|
66
|
+
# new response object with status set
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
#
|
70
|
+
# response = Response.new
|
71
|
+
# response = response.with_status(200)
|
72
|
+
# response.status # => 200
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
#
|
76
|
+
def with_status(status)
|
77
|
+
self.class.new(status, headers, body)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return response with new body
|
81
|
+
#
|
82
|
+
# @param [Object] body
|
83
|
+
# the body for the response
|
84
|
+
#
|
85
|
+
# @return [Response]
|
86
|
+
# new response with body set
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
#
|
90
|
+
# response = Response.new
|
91
|
+
# response = response.with_body('Hello')
|
92
|
+
# response.body # => 'Hello'
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
#
|
96
|
+
def with_body(body)
|
97
|
+
self.class.new(status, headers, body)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Return response with new headers
|
101
|
+
#
|
102
|
+
# @param [Hash] headers
|
103
|
+
# the header for the response
|
104
|
+
#
|
105
|
+
# @return [Response]
|
106
|
+
# new response with headers set
|
107
|
+
#
|
108
|
+
# @example
|
109
|
+
#
|
110
|
+
# response = Response.new
|
111
|
+
# response = response.with_header({'Foo' => 'Bar'})
|
112
|
+
# response.headers # => {'Foo' => 'Bar'}
|
113
|
+
#
|
114
|
+
# @api public
|
115
|
+
#
|
116
|
+
def with_headers(headers)
|
117
|
+
self.class.new(status, headers, body)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Return response with merged headers
|
121
|
+
#
|
122
|
+
# @param [Hash] headers
|
123
|
+
# the headers to merge
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
#
|
127
|
+
# response = Response.new(200, {'Foo' => 'Baz', 'John' => 'Doe'})
|
128
|
+
# response = response.merge_header({'Foo' => 'Bar'})
|
129
|
+
# response.headers # => {'Foo' => 'Bar', 'John' => 'Doe'}
|
130
|
+
#
|
131
|
+
# @api public
|
132
|
+
#
|
133
|
+
# @return [Response]
|
134
|
+
# returns new response with merged header
|
135
|
+
#
|
136
|
+
def merge_headers(headers)
|
137
|
+
self.class.new(status, self.headers.merge(headers), body)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return rack compatible array after asserting response is valid
|
141
|
+
#
|
142
|
+
# @return [Array]
|
143
|
+
# rack compatible array
|
144
|
+
#
|
145
|
+
# @raise InvalidError
|
146
|
+
# raises InvalidError when request containts undefined components
|
147
|
+
#
|
148
|
+
# @api private
|
149
|
+
#
|
150
|
+
def to_rack_response
|
151
|
+
assert_valid
|
152
|
+
rack_array
|
153
|
+
end
|
154
|
+
|
155
|
+
# Test if object is a valid response
|
156
|
+
#
|
157
|
+
# @return [true]
|
158
|
+
# if all required fields are present
|
159
|
+
#
|
160
|
+
# @return [false]
|
161
|
+
# otherwise
|
162
|
+
#
|
163
|
+
# @api private
|
164
|
+
#
|
165
|
+
def valid?
|
166
|
+
![status, headers, body].any? { |item| item.equal?(Undefined) }
|
167
|
+
end
|
168
|
+
memoize :valid?
|
169
|
+
|
170
|
+
# Return rack compatible array
|
171
|
+
#
|
172
|
+
# @return [Array]
|
173
|
+
# rack compatible array
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
#
|
177
|
+
# response = Response.new(200, {'Foo' => 'Bar'}, 'Hello World')
|
178
|
+
# response.rack_array # => [200, {'Foo' => 'Bar'}, 'Hello World']
|
179
|
+
#
|
180
|
+
# @api public
|
181
|
+
#
|
182
|
+
def rack_array
|
183
|
+
[status.code, headers, body]
|
184
|
+
end
|
185
|
+
memoize :rack_array
|
186
|
+
|
187
|
+
# Return contents of content type header
|
188
|
+
#
|
189
|
+
# @return [String]
|
190
|
+
#
|
191
|
+
# @api private
|
192
|
+
#
|
193
|
+
def content_type
|
194
|
+
headers['Content-Type']
|
195
|
+
end
|
196
|
+
|
197
|
+
# Return last modified
|
198
|
+
#
|
199
|
+
# @return [Time]
|
200
|
+
# if last modified header is present
|
201
|
+
#
|
202
|
+
# @return [nil]
|
203
|
+
# otherwise
|
204
|
+
#
|
205
|
+
# @api private
|
206
|
+
#
|
207
|
+
def last_modified
|
208
|
+
value = headers.fetch('Last-Modified') { return }
|
209
|
+
Time.httpdate(value)
|
210
|
+
end
|
211
|
+
memoize :last_modified
|
212
|
+
|
213
|
+
# Return contents of cache control header
|
214
|
+
#
|
215
|
+
# @return [String]
|
216
|
+
#
|
217
|
+
# @api private
|
218
|
+
#
|
219
|
+
def cache_control
|
220
|
+
headers['Cache-Control']
|
221
|
+
end
|
222
|
+
|
223
|
+
# Build response with a dsl like interface
|
224
|
+
#
|
225
|
+
# @example
|
226
|
+
#
|
227
|
+
# response = Response.build(200) do |response|
|
228
|
+
# response.
|
229
|
+
# with_headers('Foo' => 'Bar').
|
230
|
+
# with_body('Hello')
|
231
|
+
# end
|
232
|
+
#
|
233
|
+
# response.status # => 200
|
234
|
+
# response.headers # => {'Foo' => 'Bar' }
|
235
|
+
# response.body # => 'Hello'
|
236
|
+
#
|
237
|
+
# @return [Array]
|
238
|
+
# rack compatible array
|
239
|
+
#
|
240
|
+
# @api public
|
241
|
+
#
|
242
|
+
def self.build(status = Undefined, headers = {}, body = Undefined)
|
243
|
+
response = new(status, headers, body)
|
244
|
+
response = yield response if block_given?
|
245
|
+
response
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
# Raise error when request containts undefined components
|
251
|
+
#
|
252
|
+
# @raise InvalidError
|
253
|
+
# raises InvalidError when request containts undefined components
|
254
|
+
#
|
255
|
+
# @return [undefined]
|
256
|
+
#
|
257
|
+
# @api private
|
258
|
+
#
|
259
|
+
def assert_valid
|
260
|
+
unless valid?
|
261
|
+
raise InvalidError, "Not a valid response: #{self.inspect}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
require 'response/status'
|
267
|
+
require 'response/redirect'
|
268
|
+
require 'response/html'
|
269
|
+
require 'response/xml'
|
270
|
+
require 'response/json'
|
271
|
+
require 'response/text'
|
data/response.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'response'
|
5
|
+
gem.version = '0.0.2'
|
6
|
+
gem.authors = [ 'Markus Schirp' ]
|
7
|
+
gem.email = [ 'mbj@seonic.net' ]
|
8
|
+
gem.description = 'Build rack responses with functional style'
|
9
|
+
gem.summary = gem.description
|
10
|
+
gem.homepage = 'https://github.com/mbj/response'
|
11
|
+
|
12
|
+
gem.require_paths = [ 'lib' ]
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- {spec}/*`.split("\n")
|
15
|
+
gem.extra_rdoc_files = %w[LICENSE README.md TODO]
|
16
|
+
|
17
|
+
gem.add_dependency('ice_nine', '~> 0.7.0')
|
18
|
+
gem.add_dependency('adamantium', '~> 0.0.7')
|
19
|
+
gem.add_dependency('equalizer', '~> 0.0.5')
|
20
|
+
gem.add_dependency('abstract_type', '~> 0.0.5')
|
21
|
+
gem.add_dependency('concord', '~> 0.1.0')
|
22
|
+
end
|
data/spec/rcov.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#body' do
|
4
|
+
|
5
|
+
subject { object.body }
|
6
|
+
|
7
|
+
context 'when unset' do
|
8
|
+
let(:object) { described_class.build }
|
9
|
+
|
10
|
+
it { should be(Response::Undefined) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when set' do
|
14
|
+
let(:body) { mock('Body') }
|
15
|
+
|
16
|
+
let(:object) { described_class.build.with_body(body) }
|
17
|
+
|
18
|
+
it { should be(body) }
|
19
|
+
|
20
|
+
it_should_behave_like 'an idempotent method'
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#cache_control' do
|
4
|
+
subject { object.cache_control }
|
5
|
+
|
6
|
+
let(:object) { Response.build.with_headers(header) }
|
7
|
+
|
8
|
+
context 'when Cache-Control header is present' do
|
9
|
+
let(:header) { { 'Cache-Control' => value } }
|
10
|
+
|
11
|
+
let(:value) { mock('Value') }
|
12
|
+
|
13
|
+
it { should be(value) }
|
14
|
+
|
15
|
+
it_should_behave_like 'an idempotent method'
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when Cache-Control header is not present' do
|
19
|
+
let(:header) { {} }
|
20
|
+
|
21
|
+
it { should be(nil) }
|
22
|
+
|
23
|
+
it_should_behave_like 'an idempotent method'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '.build' do
|
4
|
+
subject { object.build(&block) }
|
5
|
+
|
6
|
+
let(:object) { described_class }
|
7
|
+
|
8
|
+
context 'without arguments and block' do
|
9
|
+
let(:block) { nil }
|
10
|
+
|
11
|
+
its(:valid?) { should be(false) }
|
12
|
+
its(:headers) { should eql({}) }
|
13
|
+
its(:status) { should be(Response::Undefined) }
|
14
|
+
its(:body) { should be(Response::Undefined) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with block' do
|
18
|
+
let(:yields) { [] }
|
19
|
+
let(:value) { mock('Value') }
|
20
|
+
let(:block) { lambda { |response| yields << response; value } }
|
21
|
+
|
22
|
+
it 'should return value from block' do
|
23
|
+
should be(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should call block with response' do
|
27
|
+
subject
|
28
|
+
yields.should eql([Response.build])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#content_type' do
|
4
|
+
subject { object.content_type }
|
5
|
+
|
6
|
+
let(:object) { Response.build.with_headers(header) }
|
7
|
+
|
8
|
+
context 'when Content-Type header is present' do
|
9
|
+
let(:header) { { 'Content-Type' => value } }
|
10
|
+
|
11
|
+
let(:value) { mock('Value') }
|
12
|
+
|
13
|
+
it { should be(value) }
|
14
|
+
|
15
|
+
it_should_behave_like 'an idempotent method'
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when Content-Type header is not present' do
|
19
|
+
let(:header) { {} }
|
20
|
+
|
21
|
+
it { should be(nil) }
|
22
|
+
|
23
|
+
it_should_behave_like 'an idempotent method'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#headers' do
|
4
|
+
|
5
|
+
subject { object.headers }
|
6
|
+
|
7
|
+
context 'when unset' do
|
8
|
+
let(:object) { described_class.build }
|
9
|
+
|
10
|
+
it { should eql({}) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when set' do
|
14
|
+
let(:headers) { mock('Headers') }
|
15
|
+
|
16
|
+
let(:object) { described_class.build.with_headers(headers) }
|
17
|
+
|
18
|
+
it { should be(headers) }
|
19
|
+
|
20
|
+
it_should_behave_like 'an idempotent method'
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response::HTML, '.build' do
|
4
|
+
subject { object.build(body) }
|
5
|
+
|
6
|
+
let(:body) { mock('Body') }
|
7
|
+
let(:object) { described_class }
|
8
|
+
|
9
|
+
its(:status) { should be(Response::Status::OK) }
|
10
|
+
its(:body) { should be(body) }
|
11
|
+
its(:headers) { should eql('Content-Type' => 'text/html; charset=UTF-8') }
|
12
|
+
|
13
|
+
it 'allows to modify response' do
|
14
|
+
object.build(body) do |response|
|
15
|
+
response.with_status(Response::Status::NOT_FOUND)
|
16
|
+
end.status.should be(Response::Status::NOT_FOUND)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response::JSON, '.build' do
|
4
|
+
subject { object.build(body) }
|
5
|
+
|
6
|
+
let(:body) { mock('Body') }
|
7
|
+
let(:object) { described_class }
|
8
|
+
|
9
|
+
its(:status) { should be(Response::Status::OK) }
|
10
|
+
its(:body) { should be(body) }
|
11
|
+
its(:headers) { should eql('Content-Type' => 'application/json; charset=UTF-8') }
|
12
|
+
|
13
|
+
it 'allows to modify response' do
|
14
|
+
object.build(body) do |response|
|
15
|
+
response.with_status(404)
|
16
|
+
end.status.should be(404)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#last_modified' do
|
4
|
+
subject { object.last_modified }
|
5
|
+
|
6
|
+
let(:object) { Response.build.with_headers(header) }
|
7
|
+
|
8
|
+
let(:time) { Time.httpdate(Time.now.httpdate) }
|
9
|
+
|
10
|
+
context 'when Content-Type header is present' do
|
11
|
+
let(:header) { { 'Last-Modified' => time.httpdate } }
|
12
|
+
|
13
|
+
it { should eql(time) }
|
14
|
+
|
15
|
+
it_should_behave_like 'an idempotent method'
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when Content-Type header is not present' do
|
19
|
+
let(:header) { {} }
|
20
|
+
|
21
|
+
it { should be(nil) }
|
22
|
+
|
23
|
+
it_should_behave_like 'an idempotent method'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#merge_headers' do
|
4
|
+
subject { object.merge_headers(update_headers) }
|
5
|
+
|
6
|
+
let(:object) { described_class.build(status, original_headers, body) }
|
7
|
+
let(:status) { Response::Status::OK }
|
8
|
+
let(:update_headers) { { 'Baz' => 'Zot' } }
|
9
|
+
let(:original_headers) { { 'Foo' => 'Bar' } }
|
10
|
+
|
11
|
+
let(:status) { mock('Status') }
|
12
|
+
let(:body) { mock('Body') }
|
13
|
+
|
14
|
+
its(:status) { should be(status) }
|
15
|
+
its(:body) { should be(body) }
|
16
|
+
its(:headers) { should eql('Foo' => 'Bar', 'Baz' => 'Zot') }
|
17
|
+
|
18
|
+
it_should_behave_like 'a functional command method'
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#rack_array' do
|
4
|
+
subject { object.rack_array }
|
5
|
+
|
6
|
+
let(:status) { Response::Status::OK }
|
7
|
+
let(:headers) { mock('Headers') }
|
8
|
+
let(:body) { mock('Body') }
|
9
|
+
|
10
|
+
let(:object) { described_class.build(status, headers, body) }
|
11
|
+
|
12
|
+
it { should eql([200, headers, body]) }
|
13
|
+
|
14
|
+
it_should_behave_like 'an idempotent method'
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response::Redirect::Found, '#build' do
|
4
|
+
|
5
|
+
subject { object.build(location) }
|
6
|
+
|
7
|
+
let(:object) { described_class }
|
8
|
+
|
9
|
+
let(:location) { mock('Location', :to_s => 'THE-LOCATION') }
|
10
|
+
|
11
|
+
its(:status) { should be(Response::Status::FOUND) }
|
12
|
+
its(:headers) { should eql('Location' => location, 'Content-Type' => 'text/plain; charset=UTF-8') }
|
13
|
+
its(:body) { should eql('You are beeing redirected to: THE-LOCATION') }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Response::Redirect::Permanent, '#build' do
|
17
|
+
|
18
|
+
subject { object.build(location) }
|
19
|
+
|
20
|
+
let(:object) { described_class }
|
21
|
+
|
22
|
+
let(:location) { mock('Location', :to_s => 'THE-LOCATION') }
|
23
|
+
|
24
|
+
its(:status) { should be(Response::Status::MOVED_PERMANENTLY) }
|
25
|
+
its(:headers) { should eql('Location' => location, 'Content-Type' => 'text/plain; charset=UTF-8') }
|
26
|
+
its(:body) { should eql('You are beeing redirected to: THE-LOCATION') }
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#status' do
|
4
|
+
|
5
|
+
subject { object.status }
|
6
|
+
|
7
|
+
context 'when unset' do
|
8
|
+
let(:object) { described_class.build }
|
9
|
+
|
10
|
+
it { should be(Response::Undefined) }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when set' do
|
14
|
+
let(:status) { mock('Status') }
|
15
|
+
|
16
|
+
let(:object) { described_class.build.with_status(status) }
|
17
|
+
|
18
|
+
it { should be(status) }
|
19
|
+
|
20
|
+
it_should_behave_like 'an idempotent method'
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response::Text, '.build' do
|
4
|
+
subject { object.build(body) }
|
5
|
+
|
6
|
+
let(:body) { mock('Body') }
|
7
|
+
let(:object) { described_class }
|
8
|
+
|
9
|
+
its(:status) { should be(Response::Status::OK) }
|
10
|
+
its(:body) { should be(body) }
|
11
|
+
its(:headers) { should eql('Content-Type' => 'text/plain; charset=UTF-8') }
|
12
|
+
|
13
|
+
it 'allows to modify response' do
|
14
|
+
object.build(body) do |response|
|
15
|
+
response.with_status(Response::Status::NOT_FOUND)
|
16
|
+
end.status.should be(Response::Status::NOT_FOUND)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#to_rack_response' do
|
4
|
+
|
5
|
+
subject { object.to_rack_response }
|
6
|
+
|
7
|
+
let(:status) { Response::Status::OK }
|
8
|
+
let(:headers) { mock('Headers') }
|
9
|
+
let(:body) { mock('Body') }
|
10
|
+
|
11
|
+
context 'with valid response' do
|
12
|
+
let(:object) { Response.build(status, headers, body) }
|
13
|
+
|
14
|
+
it { should eql([200, headers, body]) }
|
15
|
+
|
16
|
+
it_should_behave_like 'an idempotent method'
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with invalid response' do
|
20
|
+
let(:object) { Response.build }
|
21
|
+
|
22
|
+
it 'should raise error' do
|
23
|
+
expect { subject }.to raise_error(Response::InvalidError, "Not a valid response: #{object.inspect}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Response, '#valid?' do
|
4
|
+
subject { object.valid? }
|
5
|
+
|
6
|
+
context 'with status and body' do
|
7
|
+
let(:object) { described_class.build(Response::Status::OK, {}, mock('Body')) }
|
8
|
+
|
9
|
+
it { should be(true) }
|
10
|
+
|
11
|
+
it_should_behave_like 'an idempotent method'
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'without body' do
|
15
|
+
let(:object) { described_class.build.with_status(Response::Status::OK) }
|
16
|
+
|
17
|
+
it { should be(false) }
|
18
|
+
|
19
|
+
it_should_behave_like 'an idempotent method'
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'without status' do
|
23
|
+
let(:object) { described_class.build.with_body(mock('Body')) }
|
24
|
+
|
25
|
+
it { should be(false) }
|
26
|
+
|
27
|
+
it_should_behave_like 'an idempotent method'
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'without status and body' do
|
31
|
+
let(:object) { described_class.build }
|
32
|
+
|
33
|
+
it { should be(false) }
|
34
|
+
|
35
|
+
it_should_behave_like 'an idempotent method'
|
36
|
+
end
|
37
|
+
end
|