webmachine 1.6.0 → 2.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +2 -5
- data/documentation/adapters.md +2 -11
- data/examples/debugger.rb +5 -3
- data/examples/logging.rb +2 -2
- data/examples/webrick.rb +2 -2
- data/lib/webmachine/adapter.rb +0 -3
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -1
- data/lib/webmachine/adapters/rack.rb +38 -34
- data/lib/webmachine/adapters/rack_mapped.rb +1 -2
- data/lib/webmachine/adapters/webrick.rb +11 -12
- data/lib/webmachine/adapters.rb +0 -2
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +1 -1
- data/lib/webmachine/configuration.rb +1 -1
- data/lib/webmachine/constants.rb +12 -12
- data/lib/webmachine/cookie.rb +20 -18
- data/lib/webmachine/decision/conneg.rb +24 -24
- data/lib/webmachine/decision/falsey.rb +0 -1
- data/lib/webmachine/decision/flow.rb +19 -20
- data/lib/webmachine/decision/fsm.rb +4 -4
- data/lib/webmachine/decision/helpers.rb +21 -21
- data/lib/webmachine/dispatcher/route.rb +28 -28
- data/lib/webmachine/dispatcher.rb +3 -2
- data/lib/webmachine/errors.rb +7 -8
- data/lib/webmachine/etags.rb +2 -1
- data/lib/webmachine/header_negotiation.rb +5 -6
- data/lib/webmachine/headers.rb +5 -5
- data/lib/webmachine/media_type.rb +5 -5
- data/lib/webmachine/quoted_string.rb +3 -3
- data/lib/webmachine/request.rb +7 -10
- data/lib/webmachine/rescueable_exception.rb +3 -3
- data/lib/webmachine/resource/authentication.rb +3 -4
- data/lib/webmachine/resource/callbacks.rb +3 -3
- data/lib/webmachine/resource/encodings.rb +3 -9
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/response.rb +7 -9
- data/lib/webmachine/spec/adapter_lint.rb +67 -69
- data/lib/webmachine/spec/test_resource.rb +22 -22
- data/lib/webmachine/streaming/encoder.rb +3 -2
- data/lib/webmachine/streaming/io_encoder.rb +4 -3
- data/lib/webmachine/trace/fsm.rb +25 -18
- data/lib/webmachine/trace/resource_proxy.rb +10 -9
- data/lib/webmachine/trace/static/http-headers-status-v3.png +0 -0
- data/lib/webmachine/trace/trace_resource.rb +22 -24
- data/lib/webmachine/trace.rb +7 -6
- data/lib/webmachine/translation.rb +3 -3
- data/lib/webmachine/version.rb +1 -1
- metadata +52 -86
- data/.gitignore +0 -31
- data/Gemfile +0 -46
- data/Guardfile +0 -11
- data/RELEASING.md +0 -21
- data/Rakefile +0 -44
- data/lib/webmachine/adapters/httpkit.rb +0 -74
- data/lib/webmachine/adapters/reel.rb +0 -113
- data/memory_test.rb +0 -37
- data/spec/spec_helper.rb +0 -56
- data/spec/webmachine/adapter_spec.rb +0 -39
- data/spec/webmachine/adapters/httpkit_spec.rb +0 -10
- data/spec/webmachine/adapters/rack_mapped_spec.rb +0 -71
- data/spec/webmachine/adapters/rack_spec.rb +0 -62
- data/spec/webmachine/adapters/reel_spec.rb +0 -76
- data/spec/webmachine/adapters/webrick_spec.rb +0 -12
- data/spec/webmachine/application_spec.rb +0 -74
- data/spec/webmachine/chunked_body_spec.rb +0 -30
- data/spec/webmachine/configuration_spec.rb +0 -27
- data/spec/webmachine/cookie_spec.rb +0 -99
- data/spec/webmachine/decision/conneg_spec.rb +0 -166
- data/spec/webmachine/decision/falsey_spec.rb +0 -8
- data/spec/webmachine/decision/flow_spec.rb +0 -1148
- data/spec/webmachine/decision/fsm_spec.rb +0 -163
- data/spec/webmachine/decision/helpers_spec.rb +0 -216
- data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +0 -22
- data/spec/webmachine/dispatcher/route_spec.rb +0 -248
- data/spec/webmachine/dispatcher_spec.rb +0 -104
- data/spec/webmachine/errors_spec.rb +0 -13
- data/spec/webmachine/etags_spec.rb +0 -75
- data/spec/webmachine/events_spec.rb +0 -58
- data/spec/webmachine/headers_spec.rb +0 -99
- data/spec/webmachine/media_type_spec.rb +0 -85
- data/spec/webmachine/request_spec.rb +0 -273
- data/spec/webmachine/rescueable_exception_spec.rb +0 -15
- data/spec/webmachine/resource/authentication_spec.rb +0 -68
- data/spec/webmachine/response_spec.rb +0 -51
- data/spec/webmachine/trace/fsm_spec.rb +0 -37
- data/spec/webmachine/trace/resource_proxy_spec.rb +0 -34
- data/spec/webmachine/trace/trace_store_spec.rb +0 -29
- data/spec/webmachine/trace_spec.rb +0 -17
- data/webmachine.gemspec +0 -25
@@ -1,163 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Webmachine::Decision::FSM do
|
4
|
-
include_context 'default resource'
|
5
|
-
|
6
|
-
subject { described_class.new(resource, request, response) }
|
7
|
-
|
8
|
-
let(:run_with_exception) do
|
9
|
-
begin
|
10
|
-
subject.run
|
11
|
-
rescue Exception
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe 'handling of exceptions from decision methods' do
|
16
|
-
let(:UNRESCUABLE_exceptions) do
|
17
|
-
Webmachine::RescuableException::UNRESCUABLE
|
18
|
-
end
|
19
|
-
|
20
|
-
describe "rescueable exceptions" do
|
21
|
-
it 'does rescue Exception' do
|
22
|
-
allow(subject).to receive(Webmachine::Decision::Flow::START) { raise(Exception) }
|
23
|
-
expect(resource).to receive(:handle_exception).with instance_of(Exception)
|
24
|
-
expect { subject.run }.to_not raise_error
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'does rescue a failed require' do
|
28
|
-
allow(subject).to receive(Webmachine::Decision::Flow::START) { require 'laterequire' }
|
29
|
-
expect(resource).to receive(:handle_exception).with instance_of(LoadError)
|
30
|
-
expect { subject.run }.to_not raise_error
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe "UNRESCUABLE exceptions" do
|
35
|
-
shared_examples "UNRESCUABLE" do |e|
|
36
|
-
specify "#{e} is not rescued" do
|
37
|
-
allow(subject).to receive(Webmachine::Decision::Flow::START) {raise(e)}
|
38
|
-
expect(resource).to_not receive(:handle_exception).with instance_of(e)
|
39
|
-
expect { subject.run }.to raise_error(e)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
eary = Webmachine::RescuableException::UNRESCUABLE_DEFAULTS - [
|
43
|
-
Webmachine::MalformedRequest, # Webmachine rescues by default, so it won't re-raise.
|
44
|
-
SignalException # Requires raise in form 'raise SignalException, "SIGSOMESIGNAL"'.
|
45
|
-
# Haven't found a good no-op signal to use here.
|
46
|
-
]
|
47
|
-
eary.each{|e| include_examples "UNRESCUABLE", e}
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe 'handling of errors from decision methods' do
|
52
|
-
let(:error) { RuntimeError.new }
|
53
|
-
|
54
|
-
before do
|
55
|
-
allow(subject).to receive(Webmachine::Decision::Flow::START) { raise error }
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'calls resource.handle_exception' do
|
59
|
-
expect(resource).to receive(:handle_exception).with(error)
|
60
|
-
subject.run
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'calls resource.finish_request' do
|
64
|
-
expect(resource).to receive(:finish_request)
|
65
|
-
subject.run
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe 'handling of errors from resource.handle_exception' do
|
70
|
-
let(:error) { RuntimeError.new('an error message') }
|
71
|
-
|
72
|
-
before do
|
73
|
-
allow(subject).to receive(Webmachine::Decision::Flow::START) { raise }
|
74
|
-
allow(resource).to receive(:handle_exception) { raise error }
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'does not call resource.handle_exception again' do
|
78
|
-
expect(resource).to receive(:handle_exception).once { raise }
|
79
|
-
subject.run
|
80
|
-
end
|
81
|
-
|
82
|
-
it 'does not call resource.finish_request' do
|
83
|
-
expect(resource).not_to receive(:finish_request)
|
84
|
-
subject.run
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'renders an error' do
|
88
|
-
expect(Webmachine).
|
89
|
-
to receive(:render_error).
|
90
|
-
with(500, request, response, { :message => error.message })
|
91
|
-
subject.run
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
describe 'handling of exceptions from resource.finish_request' do
|
96
|
-
let(:exception) { Class.new(Exception).new }
|
97
|
-
|
98
|
-
before do
|
99
|
-
Webmachine::RescuableException.remove(exception)
|
100
|
-
allow(resource).to receive(:finish_request) { raise exception }
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'does not call resource.handle_exception' do
|
104
|
-
expect(resource).to_not receive(:handle_exception)
|
105
|
-
run_with_exception
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'does not call resource.finish_request again' do
|
109
|
-
expect(resource).to receive(:finish_request).once
|
110
|
-
run_with_exception
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe 'handling of errors from resource.finish_request' do
|
115
|
-
let(:error) { RuntimeError.new }
|
116
|
-
|
117
|
-
before do
|
118
|
-
allow(resource).to receive(:finish_request) { raise error }
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'calls resource.handle_exception' do
|
122
|
-
expect(resource).to receive(:handle_exception).with(error)
|
123
|
-
subject.run
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'does not call resource.finish_request again' do
|
127
|
-
expect(resource).to receive(:finish_request).once { raise }
|
128
|
-
subject.run
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
it "sets the response code before calling finish_request" do
|
133
|
-
resource_class.class_eval do
|
134
|
-
class << self
|
135
|
-
attr_accessor :current_response_code
|
136
|
-
end
|
137
|
-
|
138
|
-
def to_html
|
139
|
-
201
|
140
|
-
end
|
141
|
-
|
142
|
-
def finish_request
|
143
|
-
self.class.current_response_code = response.code
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
subject.run
|
148
|
-
|
149
|
-
expect(resource_class.current_response_code).to be(201)
|
150
|
-
end
|
151
|
-
|
152
|
-
it 'respects a response code set by resource.finish_request' do
|
153
|
-
resource_class.class_eval do
|
154
|
-
def finish_request
|
155
|
-
response.code = 451
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
subject.run
|
160
|
-
|
161
|
-
expect(response.code).to be(451)
|
162
|
-
end
|
163
|
-
end
|
@@ -1,216 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Webmachine::Decision::Helpers do
|
4
|
-
include_context "default resource"
|
5
|
-
subject { Webmachine::Decision::FSM.new(resource, request, response) }
|
6
|
-
|
7
|
-
def resource_with(&block)
|
8
|
-
klass = Class.new(Webmachine::Resource) do
|
9
|
-
def to_html; "test resource"; end
|
10
|
-
end
|
11
|
-
klass.module_eval(&block) if block_given?
|
12
|
-
klass.new(request, response)
|
13
|
-
end
|
14
|
-
|
15
|
-
let(:resource) { resource_with }
|
16
|
-
|
17
|
-
describe "accepting request bodies" do
|
18
|
-
let(:resource) do
|
19
|
-
resource_with do
|
20
|
-
def initialize
|
21
|
-
@accepted, @result = [], true
|
22
|
-
end
|
23
|
-
attr_accessor :accepted, :result
|
24
|
-
def content_types_accepted
|
25
|
-
(accepted || []).map {|t| Array === t ? t : [t, :accept_doc] }
|
26
|
-
end
|
27
|
-
def accept_doc; result; end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should return 415 when no types are accepted" do
|
32
|
-
expect(subject.accept_helper).to eq 415
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should return 415 when the posted type is not acceptable" do
|
36
|
-
resource.accepted = %W{application/json}
|
37
|
-
headers['Content-Type'] = "text/xml"
|
38
|
-
expect(subject.accept_helper).to eq 415
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should call the method for the first acceptable type, taking into account params" do
|
42
|
-
resource.accepted = ["application/json;v=3", ["application/json", :other]]
|
43
|
-
expect(resource).to receive(:other).and_return(true)
|
44
|
-
headers['Content-Type'] = 'application/json;v=2'
|
45
|
-
expect(subject.accept_helper).to be(true)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context "setting the Content-Length header when responding" do
|
50
|
-
[204, 205, 304].each do |code|
|
51
|
-
it "removes the header for entity-less response code #{code}" do
|
52
|
-
response.headers['Content-Length'] = '0'
|
53
|
-
response.body = nil
|
54
|
-
subject.send :respond, code
|
55
|
-
expect(response.headers).to_not include 'Content-Length'
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
(200..599).each do |code|
|
60
|
-
# 204, 205 and 304 have no bodies, 404 is set to a default
|
61
|
-
# non-zero response by Webmachine
|
62
|
-
next if [204, 205, 304, 404].include? code
|
63
|
-
|
64
|
-
it "adds the header for response code #{code} that should include an entity but has an empty body" do
|
65
|
-
response.code = code
|
66
|
-
response.body = nil
|
67
|
-
subject.send :respond, code
|
68
|
-
expect(response.headers['Content-Length']).to eq '0'
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
(200..599).each do |code|
|
73
|
-
next if [204, 205, 304].include? code
|
74
|
-
|
75
|
-
it "does not add the header when Transfer-Encoding is set on code #{code}" do
|
76
|
-
response.headers['Transfer-Encoding'] = 'chunked'
|
77
|
-
response.body = []
|
78
|
-
subject.send :respond, code
|
79
|
-
expect(response.headers).to_not include 'Content-Length'
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
describe "#encode_body" do
|
85
|
-
before { subject.run }
|
86
|
-
|
87
|
-
context "with a String body" do
|
88
|
-
before { response.body = '<body></body>' }
|
89
|
-
|
90
|
-
it "does not modify the response body" do
|
91
|
-
subject.encode_body
|
92
|
-
expect(response.body).to be_instance_of(String)
|
93
|
-
end
|
94
|
-
|
95
|
-
it "sets the Content-Length header in the response" do
|
96
|
-
subject.encode_body
|
97
|
-
expect(response.headers['Content-Length']).to eq response.body.bytesize.to_s
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
shared_examples_for "a non-String body" do
|
102
|
-
it "does not set the Content-Length header in the response" do
|
103
|
-
subject.encode_body
|
104
|
-
expect(response.headers).to_not have_key('Content-Length')
|
105
|
-
end
|
106
|
-
|
107
|
-
it "sets the Transfer-Encoding response header to chunked" do
|
108
|
-
subject.encode_body
|
109
|
-
expect(response.headers['Transfer-Encoding']).to eq 'chunked'
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
context "with an Enumerable body" do
|
114
|
-
before { response.body = ['one', 'two'] }
|
115
|
-
|
116
|
-
it "wraps the response body in an EnumerableEncoder" do
|
117
|
-
subject.encode_body
|
118
|
-
expect(response.body).to be_instance_of(Webmachine::Streaming::EnumerableEncoder)
|
119
|
-
end
|
120
|
-
|
121
|
-
it_should_behave_like "a non-String body"
|
122
|
-
end
|
123
|
-
|
124
|
-
context "with a callable body" do
|
125
|
-
before { response.body = Proc.new { 'proc' } }
|
126
|
-
|
127
|
-
it "wraps the response body in a CallableEncoder" do
|
128
|
-
subject.encode_body
|
129
|
-
expect(response.body).to be_instance_of(Webmachine::Streaming::CallableEncoder)
|
130
|
-
end
|
131
|
-
|
132
|
-
it_should_behave_like "a non-String body"
|
133
|
-
end
|
134
|
-
|
135
|
-
context "with a Fiber body" do
|
136
|
-
before { response.body = Fiber.new { Fiber.yield "foo" } }
|
137
|
-
|
138
|
-
it "wraps the response body in a FiberEncoder" do
|
139
|
-
subject.encode_body
|
140
|
-
expect(response.body).to be_instance_of(Webmachine::Streaming::FiberEncoder)
|
141
|
-
end
|
142
|
-
|
143
|
-
it_should_behave_like "a non-String body"
|
144
|
-
end
|
145
|
-
|
146
|
-
context "with a File body" do
|
147
|
-
before { response.body = File.open("spec/spec_helper.rb", "r") }
|
148
|
-
|
149
|
-
it "wraps the response body in an IOEncoder" do
|
150
|
-
subject.encode_body
|
151
|
-
expect(response.body).to be_instance_of(Webmachine::Streaming::IOEncoder)
|
152
|
-
end
|
153
|
-
|
154
|
-
it "sets the Content-Length header to the size of the file" do
|
155
|
-
subject.encode_body
|
156
|
-
expect(response.headers['Content-Length']).to eq File.stat('spec/spec_helper.rb').size.to_s
|
157
|
-
end
|
158
|
-
|
159
|
-
it "progressively yields file contents for each enumeration" do
|
160
|
-
subject.encode_body
|
161
|
-
body_size = 0
|
162
|
-
response.body.each do |chunk|
|
163
|
-
expect(chunk).to be_instance_of(String)
|
164
|
-
body_size += chunk.length
|
165
|
-
end
|
166
|
-
expect(body_size).to eq File.stat('spec/spec_helper.rb').size
|
167
|
-
end
|
168
|
-
|
169
|
-
context "when the resource provides a non-identity encoding that the client accepts" do
|
170
|
-
let(:resource) do
|
171
|
-
resource_with do
|
172
|
-
def encodings_provided
|
173
|
-
{ "deflate" => :encode_deflate, "identity" => :encode_identity }
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
let(:headers) do
|
179
|
-
Webmachine::Headers.new({"Accept-Encoding" => "deflate, identity"})
|
180
|
-
end
|
181
|
-
|
182
|
-
it_should_behave_like "a non-String body"
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
context "with a StringIO body" do
|
187
|
-
before { response.body = StringIO.new("A VERY LONG STRING, NOT") }
|
188
|
-
|
189
|
-
it "wraps the response body in an IOEncoder" do
|
190
|
-
subject.encode_body
|
191
|
-
expect(response.body).to be_instance_of(Webmachine::Streaming::IOEncoder)
|
192
|
-
end
|
193
|
-
|
194
|
-
it "sets the Content-Length header to the size of the string" do
|
195
|
-
subject.encode_body
|
196
|
-
expect(response.headers['Content-Length']).to eq response.body.size.to_s
|
197
|
-
end
|
198
|
-
|
199
|
-
context "when the resource provides a non-identity encoding that the client accepts" do
|
200
|
-
let(:resource) do
|
201
|
-
resource_with do
|
202
|
-
def encodings_provided
|
203
|
-
{ "deflate" => :encode_deflate, "identity" => :encode_identity }
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
let(:headers) do
|
209
|
-
Webmachine::Headers.new({"Accept-Encoding" => "deflate, identity"})
|
210
|
-
end
|
211
|
-
|
212
|
-
it_should_behave_like "a non-String body"
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Webmachine::Dispatcher::Route do
|
4
|
-
describe '#rfc3986_percent_decode' do
|
5
|
-
def call_subject(value)
|
6
|
-
Webmachine::Dispatcher::Route.rfc3986_percent_decode(value)
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'does not change un-encoded strings' do
|
10
|
-
expect(call_subject('this is a normal string, I think')).to eq 'this is a normal string, I think'
|
11
|
-
expect(call_subject('')).to eq ''
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'decodes percent encoded sequences' do
|
15
|
-
expect(call_subject('/tenants/esckimo+test%20%65')).to eq '/tenants/esckimo+test e'
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'leaves incorrectly encoded sequences as is' do
|
19
|
-
expect(call_subject('/tenants/esckimo+test%2%65')).to eq '/tenants/esckimo+test%2e'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,248 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
Webmachine::Dispatcher::Route.class_eval do
|
4
|
-
def warn(*msgs); end # silence warnings for tests
|
5
|
-
end
|
6
|
-
|
7
|
-
describe Webmachine::Dispatcher::Route do
|
8
|
-
let(:method) { "GET" }
|
9
|
-
let(:uri) { URI.parse("http://localhost:8080/") }
|
10
|
-
let(:routing_tokens) { nil }
|
11
|
-
let(:request){ Webmachine::Request.new(method, uri, Webmachine::Headers.new, "", routing_tokens) }
|
12
|
-
let(:resource){ Class.new(Webmachine::Resource) }
|
13
|
-
|
14
|
-
describe '#apply' do
|
15
|
-
let(:route) {
|
16
|
-
Webmachine::Dispatcher::Route.new ['hello', :string], resource, {}
|
17
|
-
}
|
18
|
-
|
19
|
-
describe 'a path_info fragment' do
|
20
|
-
let(:uri) { URI.parse("http://localhost:8080/hello/planet%20earth%20++") }
|
21
|
-
|
22
|
-
it 'should decode the value' do
|
23
|
-
route.apply(request)
|
24
|
-
expect(request.path_info).to eq({:string => 'planet earth ++'})
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
matcher :match_route do |*expected|
|
30
|
-
route = Webmachine::Dispatcher::Route.new(expected[0], Class.new(Webmachine::Resource), expected[1] || {})
|
31
|
-
match do |actual|
|
32
|
-
uri = URI.parse("http://localhost:8080")
|
33
|
-
uri.path = actual
|
34
|
-
req = Webmachine::Request.new("GET", uri, Webmachine::Headers.new, "", routing_tokens)
|
35
|
-
route.match?(req)
|
36
|
-
end
|
37
|
-
|
38
|
-
failure_message do |_|
|
39
|
-
"expected route #{expected[0].inspect} to match path #{request.uri.path}"
|
40
|
-
end
|
41
|
-
failure_message_when_negated do |_|
|
42
|
-
"expected route #{expected[0].inspect} not to match path #{request.uri.path}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
it "warns about the deprecated string splat when initializing" do
|
47
|
-
[["*"],["foo", "*"],["foo", :bar, "*"]].each do |path|
|
48
|
-
route = described_class.allocate
|
49
|
-
expect(route).to receive(:warn)
|
50
|
-
route.send :initialize, path, resource, {}
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context "matching a request" do
|
55
|
-
context "on the root path" do
|
56
|
-
subject { "/" }
|
57
|
-
it { is_expected.to match_route([]) }
|
58
|
-
it { is_expected.to match_route ['*'] }
|
59
|
-
it { is_expected.to match_route [:*] }
|
60
|
-
it { is_expected.not_to match_route %w{foo} }
|
61
|
-
it { is_expected.not_to match_route [:id] }
|
62
|
-
end
|
63
|
-
|
64
|
-
context "on a deep path" do
|
65
|
-
subject { "/foo/bar/baz" }
|
66
|
-
it { is_expected.to match_route %w{foo bar baz} }
|
67
|
-
it { is_expected.to match_route ['foo', :id, "baz"] }
|
68
|
-
it { is_expected.to match_route ['foo', :*] }
|
69
|
-
it { is_expected.to match_route [:id, :*] }
|
70
|
-
it { is_expected.not_to match_route [] }
|
71
|
-
it { is_expected.not_to match_route ['bar', :*] }
|
72
|
-
end
|
73
|
-
|
74
|
-
context "with a guard on the request method" do
|
75
|
-
let(:uri){ URI.parse("http://localhost:8080/notes") }
|
76
|
-
let(:route) do
|
77
|
-
described_class.new(
|
78
|
-
["notes"],
|
79
|
-
lambda { |request| request.method == "POST" },
|
80
|
-
resource
|
81
|
-
)
|
82
|
-
end
|
83
|
-
subject { route }
|
84
|
-
|
85
|
-
context "when guard passes" do
|
86
|
-
let(:method){ "POST" }
|
87
|
-
it { is_expected.to be_match(request) }
|
88
|
-
|
89
|
-
context "but the path match fails" do
|
90
|
-
let(:uri){ URI.parse("http://localhost:8080/other") }
|
91
|
-
it { is_expected.not_to be_match(request) }
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context "when guard fails" do
|
96
|
-
let(:method) { "GET" }
|
97
|
-
it { is_expected.not_to be_match(request) }
|
98
|
-
end
|
99
|
-
|
100
|
-
context "when the guard responds to #call" do
|
101
|
-
let(:guard_class) do
|
102
|
-
Class.new do
|
103
|
-
def initialize(method)
|
104
|
-
@method = method
|
105
|
-
end
|
106
|
-
|
107
|
-
def call(request)
|
108
|
-
request.method == @method
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
let(:route) do
|
114
|
-
described_class.new(["notes"], guard_class.new("POST"), resource)
|
115
|
-
end
|
116
|
-
|
117
|
-
context "when the guard passes" do
|
118
|
-
let(:method){ "POST" }
|
119
|
-
it { is_expected.to be_match(request) }
|
120
|
-
end
|
121
|
-
|
122
|
-
context "when the guard fails" do
|
123
|
-
# let(:method){ "GET" }
|
124
|
-
it { is_expected.not_to be_match(request) }
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
context "with a request with explicitly specified routing tokens" do
|
130
|
-
subject { "/some/route/foo/bar" }
|
131
|
-
let(:routing_tokens) { ["foo", "bar"] }
|
132
|
-
it { is_expected.to match_route(["foo", "bar"]) }
|
133
|
-
it { is_expected.to match_route(["foo", :id]) }
|
134
|
-
it { is_expected.to match_route ['*'] }
|
135
|
-
it { is_expected.to match_route [:*] }
|
136
|
-
it { is_expected.not_to match_route(["some", "route", "foo", "bar"]) }
|
137
|
-
it { is_expected.not_to match_route %w{foo} }
|
138
|
-
it { is_expected.not_to match_route [:id] }
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
context "applying bindings" do
|
143
|
-
context "on the root path" do
|
144
|
-
subject { described_class.new([], resource) }
|
145
|
-
before { subject.apply(request) }
|
146
|
-
|
147
|
-
it "should assign the dispatched path to the empty string" do
|
148
|
-
expect(request.disp_path).to eq("")
|
149
|
-
end
|
150
|
-
|
151
|
-
it "should assign empty bindings" do
|
152
|
-
expect(request.path_info).to eq({})
|
153
|
-
end
|
154
|
-
|
155
|
-
it "should assign empty path tokens" do
|
156
|
-
expect(request.path_tokens).to eq([])
|
157
|
-
end
|
158
|
-
|
159
|
-
context "with extra user-defined bindings" do
|
160
|
-
subject { described_class.new([], resource, "bar" => "baz") }
|
161
|
-
|
162
|
-
it "should assign the user-defined bindings" do
|
163
|
-
expect(request.path_info).to eq({"bar" => "baz"})
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
context "with a splat" do
|
168
|
-
subject { described_class.new([:*], resource) }
|
169
|
-
|
170
|
-
it "should assign empty path tokens" do
|
171
|
-
expect(request.path_tokens).to eq([])
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
context "with a deprecated splat string" do
|
176
|
-
subject { described_class.new(['*'], resource) }
|
177
|
-
|
178
|
-
it "should assign empty path tokens" do
|
179
|
-
expect(request.path_tokens).to eq([])
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
context "on a deep path" do
|
184
|
-
subject { described_class.new(%w{foo bar baz}, resource) }
|
185
|
-
let(:uri) { URI.parse("http://localhost:8080/foo/bar/baz") }
|
186
|
-
before { subject.apply(request) }
|
187
|
-
|
188
|
-
it "should assign the dispatched path as the path past the initial slash" do
|
189
|
-
expect(request.disp_path).to eq("foo/bar/baz")
|
190
|
-
end
|
191
|
-
|
192
|
-
it "should assign empty bindings" do
|
193
|
-
expect(request.path_info).to eq({})
|
194
|
-
end
|
195
|
-
|
196
|
-
it "should assign empty path tokens" do
|
197
|
-
expect(request.path_tokens).to eq([])
|
198
|
-
end
|
199
|
-
|
200
|
-
context "with path variables" do
|
201
|
-
subject { described_class.new(['foo', :id, 'baz'], resource) }
|
202
|
-
|
203
|
-
it "should assign the path variables in the bindings" do
|
204
|
-
expect(request.path_info).to eq({:id => "bar"})
|
205
|
-
end
|
206
|
-
end
|
207
|
-
context "with regex" do
|
208
|
-
subject { described_class.new([/foo/, /(.*)/, 'baz'], resource) }
|
209
|
-
|
210
|
-
it "should assign the captures path variables" do
|
211
|
-
expect(request.path_info).to eq({:captures => ["bar"]})
|
212
|
-
end
|
213
|
-
end
|
214
|
-
context "with multi-capture regex" do
|
215
|
-
subject { described_class.new([/foo/, /(.*)/, /baz\.(.*)/], resource) }
|
216
|
-
let(:uri) { URI.parse("http://localhost:8080/foo/bar/baz.json") }
|
217
|
-
|
218
|
-
it "should assign the captures path variables" do
|
219
|
-
expect(request.path_info).to eq({:captures => ["bar", "json"]})
|
220
|
-
end
|
221
|
-
end
|
222
|
-
context "with named capture regex" do
|
223
|
-
subject { described_class.new(['foo', :bar, /(?<baz>[^.]+)\.(?<format>.*)/], resource) }
|
224
|
-
let(:uri) { URI.parse("http://localhost:8080/foo/bar/baz.json") }
|
225
|
-
|
226
|
-
it "should assign the captures path variables" do
|
227
|
-
expect(request.path_info).to eq({bar: 'bar', baz: 'baz', format: "json"})
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
context "with a splat" do
|
232
|
-
subject { described_class.new(['foo', :*], resource) }
|
233
|
-
|
234
|
-
it "should capture the path tokens matched by the splat" do
|
235
|
-
expect(request.path_tokens).to eq(%w{ bar baz })
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
context "with a deprecated splat string" do
|
240
|
-
subject { described_class.new(%w{foo *}, resource) }
|
241
|
-
|
242
|
-
it "should capture the path tokens matched by the splat" do
|
243
|
-
expect(request.path_tokens).to eq(%w{ bar baz })
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|