webmachine 1.2.2 → 1.3.0
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 +7 -0
- data/.gitignore +4 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +13 -11
- data/README.md +85 -89
- data/Rakefile +0 -1
- data/documentation/adapters.md +39 -0
- data/documentation/authentication-and-authorization.md +37 -0
- data/documentation/configurator.md +19 -0
- data/documentation/error-handling.md +86 -0
- data/documentation/examples.md +215 -0
- data/documentation/how-it-works.md +76 -0
- data/documentation/routes.md +97 -0
- data/documentation/validation.md +159 -0
- data/documentation/versioning-apis.md +74 -0
- data/documentation/visual-debugger.md +38 -0
- data/examples/application.rb +2 -2
- data/examples/debugger.rb +1 -1
- data/lib/webmachine.rb +3 -1
- data/lib/webmachine/adapter.rb +7 -13
- data/lib/webmachine/adapters.rb +1 -2
- data/lib/webmachine/adapters/httpkit.rb +74 -0
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
- data/lib/webmachine/adapters/rack.rb +37 -21
- data/lib/webmachine/adapters/reel.rb +21 -23
- data/lib/webmachine/adapters/webrick.rb +16 -16
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +3 -4
- data/lib/webmachine/constants.rb +75 -0
- data/lib/webmachine/decision/conneg.rb +12 -10
- data/lib/webmachine/decision/flow.rb +31 -21
- data/lib/webmachine/decision/fsm.rb +10 -18
- data/lib/webmachine/decision/helpers.rb +9 -37
- data/lib/webmachine/dispatcher.rb +13 -10
- data/lib/webmachine/dispatcher/route.rb +18 -8
- data/lib/webmachine/errors.rb +7 -1
- data/lib/webmachine/header_negotiation.rb +25 -0
- data/lib/webmachine/headers.rb +7 -2
- data/lib/webmachine/locale/en.yml +7 -5
- data/lib/webmachine/media_type.rb +10 -8
- data/lib/webmachine/request.rb +44 -15
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/resource/callbacks.rb +6 -4
- data/lib/webmachine/spec/IO_response.body +1 -0
- data/lib/webmachine/spec/adapter_lint.rb +70 -36
- data/lib/webmachine/spec/test_resource.rb +10 -4
- data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
- data/lib/webmachine/streaming/io_encoder.rb +6 -0
- data/lib/webmachine/trace.rb +1 -0
- data/lib/webmachine/trace/fsm.rb +20 -10
- data/lib/webmachine/trace/resource_proxy.rb +2 -0
- data/lib/webmachine/translation.rb +2 -1
- data/lib/webmachine/version.rb +3 -3
- data/memory_test.rb +37 -0
- data/spec/spec_helper.rb +9 -9
- data/spec/webmachine/adapter_spec.rb +14 -15
- data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
- data/spec/webmachine/adapters/rack_spec.rb +6 -6
- data/spec/webmachine/adapters/reel_spec.rb +15 -11
- data/spec/webmachine/adapters/webrick_spec.rb +2 -2
- data/spec/webmachine/application_spec.rb +18 -17
- data/spec/webmachine/chunked_body_spec.rb +3 -3
- data/spec/webmachine/configuration_spec.rb +5 -5
- data/spec/webmachine/cookie_spec.rb +13 -13
- data/spec/webmachine/decision/conneg_spec.rb +48 -42
- data/spec/webmachine/decision/falsey_spec.rb +4 -4
- data/spec/webmachine/decision/flow_spec.rb +194 -144
- data/spec/webmachine/decision/fsm_spec.rb +17 -17
- data/spec/webmachine/decision/helpers_spec.rb +20 -20
- data/spec/webmachine/dispatcher/route_spec.rb +73 -27
- data/spec/webmachine/dispatcher_spec.rb +34 -24
- data/spec/webmachine/errors_spec.rb +1 -1
- data/spec/webmachine/etags_spec.rb +19 -19
- data/spec/webmachine/events_spec.rb +6 -6
- data/spec/webmachine/headers_spec.rb +14 -14
- data/spec/webmachine/media_type_spec.rb +36 -36
- data/spec/webmachine/request_spec.rb +33 -33
- data/spec/webmachine/resource/authentication_spec.rb +6 -6
- data/spec/webmachine/response_spec.rb +12 -12
- data/spec/webmachine/trace/fsm_spec.rb +8 -8
- data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
- data/spec/webmachine/trace/trace_store_spec.rb +5 -5
- data/spec/webmachine/trace_spec.rb +3 -3
- data/webmachine.gemspec +2 -6
- metadata +48 -206
- data/lib/webmachine/adapters/hatetepe.rb +0 -108
- data/lib/webmachine/adapters/mongrel.rb +0 -127
- data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
- data/lib/webmachine/fiber18.rb +0 -88
- data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
- data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
@@ -6,63 +6,63 @@ describe Webmachine::Decision::FSM do
|
|
6
6
|
subject { described_class.new(resource, request, response) }
|
7
7
|
|
8
8
|
describe 'handling of exceptions from decision methods' do
|
9
|
-
let(:exception) {
|
9
|
+
let(:exception) { RuntimeError.new }
|
10
10
|
|
11
11
|
before do
|
12
|
-
subject.
|
12
|
+
allow(subject).to receive(Webmachine::Decision::Flow::START) { raise exception }
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'calls resource.handle_exception' do
|
16
|
-
resource.
|
16
|
+
expect(resource).to receive(:handle_exception).with(exception)
|
17
17
|
subject.run
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'calls resource.finish_request' do
|
21
|
-
resource.
|
21
|
+
expect(resource).to receive(:finish_request)
|
22
22
|
subject.run
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
describe 'handling of exceptions from resource.handle_exception' do
|
27
|
-
let(:exception) {
|
27
|
+
let(:exception) { RuntimeError.new('an error message') }
|
28
28
|
|
29
29
|
before do
|
30
|
-
subject.
|
31
|
-
resource.
|
30
|
+
allow(subject).to receive(Webmachine::Decision::Flow::START) { raise }
|
31
|
+
allow(resource).to receive(:handle_exception) { raise exception }
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'does not call resource.handle_exception again' do
|
35
|
-
resource.
|
35
|
+
expect(resource).to receive(:handle_exception).once { raise }
|
36
36
|
subject.run
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'does not call resource.finish_request' do
|
40
|
-
resource.
|
40
|
+
expect(resource).not_to receive(:finish_request)
|
41
41
|
subject.run
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'renders an error' do
|
45
|
-
Webmachine.
|
46
|
-
|
45
|
+
expect(Webmachine).
|
46
|
+
to receive(:render_error).
|
47
47
|
with(500, request, response, { :message => exception.message })
|
48
48
|
subject.run
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
describe 'handling of exceptions from resource.finish_request' do
|
53
|
-
let(:exception) {
|
53
|
+
let(:exception) { RuntimeError.new }
|
54
54
|
|
55
55
|
before do
|
56
|
-
resource.
|
56
|
+
allow(resource).to receive(:finish_request) { raise exception }
|
57
57
|
end
|
58
58
|
|
59
59
|
it 'calls resource.handle_exception' do
|
60
|
-
resource.
|
60
|
+
expect(resource).to receive(:handle_exception).with(exception)
|
61
61
|
subject.run
|
62
62
|
end
|
63
63
|
|
64
64
|
it 'does not call resource.finish_request again' do
|
65
|
-
resource.
|
65
|
+
expect(resource).to receive(:finish_request).once { raise }
|
66
66
|
subject.run
|
67
67
|
end
|
68
68
|
end
|
@@ -84,7 +84,7 @@ describe Webmachine::Decision::FSM do
|
|
84
84
|
|
85
85
|
subject.run
|
86
86
|
|
87
|
-
resource_class.current_response_code.
|
87
|
+
expect(resource_class.current_response_code).to be(201)
|
88
88
|
end
|
89
89
|
|
90
90
|
it 'respects a response code set by resource.finish_request' do
|
@@ -96,6 +96,6 @@ describe Webmachine::Decision::FSM do
|
|
96
96
|
|
97
97
|
subject.run
|
98
98
|
|
99
|
-
response.code.
|
99
|
+
expect(response.code).to be(451)
|
100
100
|
end
|
101
101
|
end
|
@@ -29,20 +29,20 @@ describe Webmachine::Decision::Helpers do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
it "should return 415 when no types are accepted" do
|
32
|
-
subject.accept_helper.
|
32
|
+
expect(subject.accept_helper).to eq 415
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should return 415 when the posted type is not acceptable" do
|
36
36
|
resource.accepted = %W{application/json}
|
37
37
|
headers['Content-Type'] = "text/xml"
|
38
|
-
subject.accept_helper.
|
38
|
+
expect(subject.accept_helper).to eq 415
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should call the method for the first acceptable type, taking into account params" do
|
42
42
|
resource.accepted = ["application/json;v=3", ["application/json", :other]]
|
43
|
-
resource.
|
43
|
+
expect(resource).to receive(:other).and_return(true)
|
44
44
|
headers['Content-Type'] = 'application/json;v=2'
|
45
|
-
subject.accept_helper.
|
45
|
+
expect(subject.accept_helper).to be(true)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -52,7 +52,7 @@ describe Webmachine::Decision::Helpers do
|
|
52
52
|
response.headers['Content-Length'] = '0'
|
53
53
|
response.body = nil
|
54
54
|
subject.send :respond, code
|
55
|
-
response.headers.
|
55
|
+
expect(response.headers).to_not include 'Content-Length'
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -65,7 +65,7 @@ describe Webmachine::Decision::Helpers do
|
|
65
65
|
response.code = code
|
66
66
|
response.body = nil
|
67
67
|
subject.send :respond, code
|
68
|
-
response.headers['Content-Length'].
|
68
|
+
expect(response.headers['Content-Length']).to eq '0'
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
@@ -76,7 +76,7 @@ describe Webmachine::Decision::Helpers do
|
|
76
76
|
response.headers['Transfer-Encoding'] = 'chunked'
|
77
77
|
response.body = []
|
78
78
|
subject.send :respond, code
|
79
|
-
response.headers.
|
79
|
+
expect(response.headers).to_not include 'Content-Length'
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -89,24 +89,24 @@ describe Webmachine::Decision::Helpers do
|
|
89
89
|
|
90
90
|
it "does not modify the response body" do
|
91
91
|
subject.encode_body
|
92
|
-
|
92
|
+
expect(response.body).to be_instance_of(String)
|
93
93
|
end
|
94
94
|
|
95
95
|
it "sets the Content-Length header in the response" do
|
96
96
|
subject.encode_body
|
97
|
-
response.headers['Content-Length'].
|
97
|
+
expect(response.headers['Content-Length']).to eq response.body.bytesize.to_s
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
101
|
shared_examples_for "a non-String body" do
|
102
102
|
it "does not set the Content-Length header in the response" do
|
103
103
|
subject.encode_body
|
104
|
-
response.headers.
|
104
|
+
expect(response.headers).to_not have_key('Content-Length')
|
105
105
|
end
|
106
106
|
|
107
107
|
it "sets the Transfer-Encoding response header to chunked" do
|
108
108
|
subject.encode_body
|
109
|
-
response.headers['Transfer-Encoding'].
|
109
|
+
expect(response.headers['Transfer-Encoding']).to eq 'chunked'
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
@@ -115,7 +115,7 @@ describe Webmachine::Decision::Helpers do
|
|
115
115
|
|
116
116
|
it "wraps the response body in an EnumerableEncoder" do
|
117
117
|
subject.encode_body
|
118
|
-
Webmachine::Streaming::EnumerableEncoder
|
118
|
+
expect(response.body).to be_instance_of(Webmachine::Streaming::EnumerableEncoder)
|
119
119
|
end
|
120
120
|
|
121
121
|
it_should_behave_like "a non-String body"
|
@@ -126,7 +126,7 @@ describe Webmachine::Decision::Helpers do
|
|
126
126
|
|
127
127
|
it "wraps the response body in a CallableEncoder" do
|
128
128
|
subject.encode_body
|
129
|
-
Webmachine::Streaming::CallableEncoder
|
129
|
+
expect(response.body).to be_instance_of(Webmachine::Streaming::CallableEncoder)
|
130
130
|
end
|
131
131
|
|
132
132
|
it_should_behave_like "a non-String body"
|
@@ -137,7 +137,7 @@ describe Webmachine::Decision::Helpers do
|
|
137
137
|
|
138
138
|
it "wraps the response body in a FiberEncoder" do
|
139
139
|
subject.encode_body
|
140
|
-
Webmachine::Streaming::FiberEncoder
|
140
|
+
expect(response.body).to be_instance_of(Webmachine::Streaming::FiberEncoder)
|
141
141
|
end
|
142
142
|
|
143
143
|
it_should_behave_like "a non-String body"
|
@@ -148,22 +148,22 @@ describe Webmachine::Decision::Helpers do
|
|
148
148
|
|
149
149
|
it "wraps the response body in an IOEncoder" do
|
150
150
|
subject.encode_body
|
151
|
-
Webmachine::Streaming::IOEncoder
|
151
|
+
expect(response.body).to be_instance_of(Webmachine::Streaming::IOEncoder)
|
152
152
|
end
|
153
153
|
|
154
154
|
it "sets the Content-Length header to the size of the file" do
|
155
155
|
subject.encode_body
|
156
|
-
response.headers['Content-Length'].
|
156
|
+
expect(response.headers['Content-Length']).to eq File.stat('spec/spec_helper.rb').size.to_s
|
157
157
|
end
|
158
158
|
|
159
159
|
it "progressively yields file contents for each enumeration" do
|
160
160
|
subject.encode_body
|
161
161
|
body_size = 0
|
162
162
|
response.body.each do |chunk|
|
163
|
-
chunk.
|
163
|
+
expect(chunk).to be_instance_of(String)
|
164
164
|
body_size += chunk.length
|
165
165
|
end
|
166
|
-
body_size.
|
166
|
+
expect(body_size).to eq File.stat('spec/spec_helper.rb').size
|
167
167
|
end
|
168
168
|
|
169
169
|
context "when the resource provides a non-identity encoding that the client accepts" do
|
@@ -188,12 +188,12 @@ describe Webmachine::Decision::Helpers do
|
|
188
188
|
|
189
189
|
it "wraps the response body in an IOEncoder" do
|
190
190
|
subject.encode_body
|
191
|
-
Webmachine::Streaming::IOEncoder
|
191
|
+
expect(response.body).to be_instance_of(Webmachine::Streaming::IOEncoder)
|
192
192
|
end
|
193
193
|
|
194
194
|
it "sets the Content-Length header to the size of the string" do
|
195
195
|
subject.encode_body
|
196
|
-
response.headers['Content-Length'].
|
196
|
+
expect(response.headers['Content-Length']).to eq response.body.size.to_s
|
197
197
|
end
|
198
198
|
|
199
199
|
context "when the resource provides a non-identity encoding that the client accepts" do
|
@@ -1,11 +1,32 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
Webmachine::Dispatcher::Route.class_eval do
|
4
|
+
def warn(*msgs); end # silence warnings for tests
|
5
|
+
end
|
6
|
+
|
3
7
|
describe Webmachine::Dispatcher::Route do
|
4
8
|
let(:method) { "GET" }
|
5
9
|
let(:uri) { URI.parse("http://localhost:8080/") }
|
6
10
|
let(:request){ Webmachine::Request.new(method, uri, Webmachine::Headers.new, "") }
|
7
11
|
let(:resource){ Class.new(Webmachine::Resource) }
|
8
12
|
|
13
|
+
describe '#apply' do
|
14
|
+
let(:route) {
|
15
|
+
Webmachine::Dispatcher::Route.new ['hello', :string], resource, {}
|
16
|
+
}
|
17
|
+
|
18
|
+
describe 'a path_info fragment' do
|
19
|
+
before do
|
20
|
+
uri.path = '/hello/planet%20earth%20++'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should decode the value' do
|
24
|
+
route.apply(request)
|
25
|
+
expect(request.path_info).to eq({:string => 'planet earth ++'})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
9
30
|
matcher :match_route do |*expected|
|
10
31
|
route = Webmachine::Dispatcher::Route.new(expected[0], Class.new(Webmachine::Resource), expected[1] || {})
|
11
32
|
match do |actual|
|
@@ -13,31 +34,40 @@ describe Webmachine::Dispatcher::Route do
|
|
13
34
|
route.match?(request)
|
14
35
|
end
|
15
36
|
|
16
|
-
|
37
|
+
failure_message do |_|
|
17
38
|
"expected route #{expected[0].inspect} to match path #{request.uri.path}"
|
18
39
|
end
|
19
|
-
|
40
|
+
failure_message_when_negated do |_|
|
20
41
|
"expected route #{expected[0].inspect} not to match path #{request.uri.path}"
|
21
42
|
end
|
22
43
|
end
|
23
44
|
|
45
|
+
it "warns about the deprecated string splat when initializing" do
|
46
|
+
[["*"],["foo", "*"],["foo", :bar, "*"]].each do |path|
|
47
|
+
route = described_class.allocate
|
48
|
+
expect(route).to receive(:warn)
|
49
|
+
route.send :initialize, path, resource, {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
24
53
|
context "matching a request" do
|
25
54
|
context "on the root path" do
|
26
55
|
subject { "/" }
|
27
|
-
it {
|
28
|
-
it {
|
29
|
-
it {
|
30
|
-
it {
|
56
|
+
it { is_expected.to match_route([]) }
|
57
|
+
it { is_expected.to match_route ['*'] }
|
58
|
+
it { is_expected.to match_route [:*] }
|
59
|
+
it { is_expected.not_to match_route %w{foo} }
|
60
|
+
it { is_expected.not_to match_route [:id] }
|
31
61
|
end
|
32
62
|
|
33
63
|
context "on a deep path" do
|
34
64
|
subject { "/foo/bar/baz" }
|
35
|
-
it {
|
36
|
-
it {
|
37
|
-
it {
|
38
|
-
it {
|
39
|
-
it {
|
40
|
-
it {
|
65
|
+
it { is_expected.to match_route %w{foo bar baz} }
|
66
|
+
it { is_expected.to match_route ['foo', :id, "baz"] }
|
67
|
+
it { is_expected.to match_route ['foo', :*] }
|
68
|
+
it { is_expected.to match_route [:id, :*] }
|
69
|
+
it { is_expected.not_to match_route [] }
|
70
|
+
it { is_expected.not_to match_route ['bar', :*] }
|
41
71
|
end
|
42
72
|
|
43
73
|
context "with a guard on the request method" do
|
@@ -53,17 +83,17 @@ describe Webmachine::Dispatcher::Route do
|
|
53
83
|
|
54
84
|
context "when guard passes" do
|
55
85
|
let(:method){ "POST" }
|
56
|
-
it {
|
86
|
+
it { is_expected.to be_match(request) }
|
57
87
|
|
58
88
|
context "but the path match fails" do
|
59
89
|
let(:uri){ URI.parse("http://localhost:8080/other") }
|
60
|
-
it {
|
90
|
+
it { is_expected.not_to be_match(request) }
|
61
91
|
end
|
62
92
|
end
|
63
93
|
|
64
94
|
context "when guard fails" do
|
65
95
|
let(:method) { "GET" }
|
66
|
-
it {
|
96
|
+
it { is_expected.not_to be_match(request) }
|
67
97
|
end
|
68
98
|
|
69
99
|
context "when the guard responds to #call" do
|
@@ -85,12 +115,12 @@ describe Webmachine::Dispatcher::Route do
|
|
85
115
|
|
86
116
|
context "when the guard passes" do
|
87
117
|
let(:method){ "POST" }
|
88
|
-
it {
|
118
|
+
it { is_expected.to be_match(request) }
|
89
119
|
end
|
90
120
|
|
91
121
|
context "when the guard fails" do
|
92
122
|
# let(:method){ "GET" }
|
93
|
-
it {
|
123
|
+
it { is_expected.not_to be_match(request) }
|
94
124
|
end
|
95
125
|
end
|
96
126
|
end
|
@@ -102,30 +132,38 @@ describe Webmachine::Dispatcher::Route do
|
|
102
132
|
before { subject.apply(request) }
|
103
133
|
|
104
134
|
it "should assign the dispatched path to the empty string" do
|
105
|
-
request.disp_path.
|
135
|
+
expect(request.disp_path).to eq("")
|
106
136
|
end
|
107
137
|
|
108
138
|
it "should assign empty bindings" do
|
109
|
-
request.path_info.
|
139
|
+
expect(request.path_info).to eq({})
|
110
140
|
end
|
111
141
|
|
112
142
|
it "should assign empty path tokens" do
|
113
|
-
request.path_tokens.
|
143
|
+
expect(request.path_tokens).to eq([])
|
114
144
|
end
|
115
145
|
|
116
146
|
context "with extra user-defined bindings" do
|
117
147
|
subject { described_class.new([], resource, "bar" => "baz") }
|
118
148
|
|
119
149
|
it "should assign the user-defined bindings" do
|
120
|
-
request.path_info.
|
150
|
+
expect(request.path_info).to eq({"bar" => "baz"})
|
121
151
|
end
|
122
152
|
end
|
123
153
|
|
124
154
|
context "with a splat" do
|
155
|
+
subject { described_class.new([:*], resource) }
|
156
|
+
|
157
|
+
it "should assign empty path tokens" do
|
158
|
+
expect(request.path_tokens).to eq([])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "with a deprecated splat string" do
|
125
163
|
subject { described_class.new(['*'], resource) }
|
126
164
|
|
127
165
|
it "should assign empty path tokens" do
|
128
|
-
request.path_tokens.
|
166
|
+
expect(request.path_tokens).to eq([])
|
129
167
|
end
|
130
168
|
end
|
131
169
|
end
|
@@ -135,30 +173,38 @@ describe Webmachine::Dispatcher::Route do
|
|
135
173
|
before { request.uri.path = "/foo/bar/baz"; subject.apply(request) }
|
136
174
|
|
137
175
|
it "should assign the dispatched path as the path past the initial slash" do
|
138
|
-
request.disp_path.
|
176
|
+
expect(request.disp_path).to eq("foo/bar/baz")
|
139
177
|
end
|
140
178
|
|
141
179
|
it "should assign empty bindings" do
|
142
|
-
request.path_info.
|
180
|
+
expect(request.path_info).to eq({})
|
143
181
|
end
|
144
182
|
|
145
183
|
it "should assign empty path tokens" do
|
146
|
-
request.path_tokens.
|
184
|
+
expect(request.path_tokens).to eq([])
|
147
185
|
end
|
148
186
|
|
149
187
|
context "with path variables" do
|
150
188
|
subject { described_class.new(['foo', :id, 'baz'], resource) }
|
151
189
|
|
152
190
|
it "should assign the path variables in the bindings" do
|
153
|
-
request.path_info.
|
191
|
+
expect(request.path_info).to eq({:id => "bar"})
|
154
192
|
end
|
155
193
|
end
|
156
194
|
|
157
195
|
context "with a splat" do
|
196
|
+
subject { described_class.new(['foo', :*], resource) }
|
197
|
+
|
198
|
+
it "should capture the path tokens matched by the splat" do
|
199
|
+
expect(request.path_tokens).to eq(%w{ bar baz })
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context "with a deprecated splat string" do
|
158
204
|
subject { described_class.new(%w{foo *}, resource) }
|
159
205
|
|
160
206
|
it "should capture the path tokens matched by the splat" do
|
161
|
-
request.path_tokens.
|
207
|
+
expect(request.path_tokens).to eq(%w{ bar baz })
|
162
208
|
end
|
163
209
|
end
|
164
210
|
end
|
@@ -14,66 +14,76 @@ describe Webmachine::Dispatcher do
|
|
14
14
|
def to_html; "goodbye, cruel world"; end
|
15
15
|
end
|
16
16
|
end
|
17
|
-
let(:fsm){
|
17
|
+
let(:fsm){ double }
|
18
18
|
|
19
19
|
before { dispatcher.reset }
|
20
20
|
|
21
21
|
it "should add routes from a block" do
|
22
22
|
_resource = resource
|
23
|
-
Webmachine.routes do
|
24
|
-
add [
|
25
|
-
end.
|
26
|
-
dispatcher.routes.
|
23
|
+
expect(Webmachine.routes do
|
24
|
+
add [:*], _resource
|
25
|
+
end).to eq(Webmachine)
|
26
|
+
expect(dispatcher.routes.size).to eq(1)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should add routes" do
|
30
30
|
expect {
|
31
|
-
dispatcher.add_route [
|
31
|
+
dispatcher.add_route [:*], resource
|
32
32
|
}.to_not raise_error
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should have add_route return the newly created route" do
|
36
|
-
route = dispatcher.add_route [
|
37
|
-
route.
|
36
|
+
route = dispatcher.add_route [:*], resource
|
37
|
+
expect(route).to be_instance_of Webmachine::Dispatcher::Route
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should route to the proper resource" do
|
41
41
|
dispatcher.add_route ["goodbye"], resource2
|
42
|
-
dispatcher.add_route [
|
43
|
-
Webmachine::Decision::FSM.
|
44
|
-
fsm.
|
42
|
+
dispatcher.add_route [:*], resource
|
43
|
+
expect(Webmachine::Decision::FSM).to receive(:new).with(instance_of(resource), request, response).and_return(fsm)
|
44
|
+
expect(fsm).to receive(:run)
|
45
45
|
dispatcher.dispatch(request, response)
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should apply route to request before creating the resource" do
|
49
|
-
route = dispatcher.add_route [
|
49
|
+
route = dispatcher.add_route [:*], resource
|
50
50
|
applied = false
|
51
51
|
|
52
|
-
route.
|
53
|
-
resource.
|
54
|
-
applied.
|
52
|
+
expect(route).to receive(:apply) { applied = true }
|
53
|
+
expect(resource).to(receive(:new) do
|
54
|
+
expect(applied).to be(true)
|
55
55
|
resource2.new(request, response)
|
56
|
-
end
|
56
|
+
end)
|
57
57
|
|
58
58
|
dispatcher.dispatch(request, response)
|
59
59
|
end
|
60
60
|
|
61
61
|
it "should add routes with guards" do
|
62
62
|
dispatcher.add [], lambda {|req| req.method == "POST" }, resource
|
63
|
-
dispatcher.add [
|
63
|
+
dispatcher.add [:*], resource2 do |req|
|
64
64
|
!req.query.empty?
|
65
65
|
end
|
66
66
|
request.uri.query = "?foo=bar"
|
67
|
-
dispatcher.routes.
|
68
|
-
Webmachine::Decision::FSM.
|
69
|
-
fsm.
|
67
|
+
expect(dispatcher.routes.size).to eq(2)
|
68
|
+
expect(Webmachine::Decision::FSM).to receive(:new).with(instance_of(resource2), request, response).and_return(fsm)
|
69
|
+
expect(fsm).to receive(:run)
|
70
|
+
dispatcher.dispatch(request, response)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should respond with a valid resource for a 404" do
|
70
74
|
dispatcher.dispatch(request, response)
|
75
|
+
expect(response.code).to eq(404)
|
76
|
+
expect(response.body).to_not be_empty
|
77
|
+
expect(response.headers).to have_key('Content-Length')
|
78
|
+
expect(response.headers).to have_key('Date')
|
71
79
|
end
|
72
80
|
|
73
|
-
it "should respond with valid resource
|
81
|
+
it "should respond with a valid resource for a 404 with a custom Accept header" do
|
82
|
+
request.headers['Accept'] = "application/json"
|
74
83
|
dispatcher.dispatch(request, response)
|
75
|
-
response.code.
|
76
|
-
response.body.
|
77
|
-
response.headers.
|
84
|
+
expect(response.code).to eq(404)
|
85
|
+
expect(response.body).to_not be_empty
|
86
|
+
expect(response.headers).to have_key('Content-Length')
|
87
|
+
expect(response.headers).to have_key('Date')
|
78
88
|
end
|
79
89
|
end
|