response 0.0.2
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.
- 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
|