webmachine 1.2.2 → 1.6.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 +3 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +20 -15
- data/README.md +89 -91
- data/RELEASING.md +21 -0
- data/Rakefile +5 -21
- data/documentation/adapters.md +41 -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 +224 -0
- data/documentation/how-it-works.md +76 -0
- data/documentation/routes.md +112 -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 +70 -25
- data/lib/webmachine/adapters/rack_mapped.rb +42 -0
- data/lib/webmachine/adapters/reel.rb +22 -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/configuration.rb +1 -1
- data/lib/webmachine/constants.rb +75 -0
- data/lib/webmachine/decision/conneg.rb +12 -10
- data/lib/webmachine/decision/flow.rb +42 -32
- data/lib/webmachine/decision/fsm.rb +14 -21
- data/lib/webmachine/decision/helpers.rb +10 -38
- data/lib/webmachine/dispatcher.rb +13 -10
- data/lib/webmachine/dispatcher/route.rb +45 -9
- data/lib/webmachine/errors.rb +9 -3
- data/lib/webmachine/events.rb +2 -2
- data/lib/webmachine/header_negotiation.rb +25 -0
- data/lib/webmachine/headers.rb +8 -3
- data/lib/webmachine/locale/en.yml +7 -5
- data/lib/webmachine/media_type.rb +10 -8
- data/lib/webmachine/request.rb +67 -26
- data/lib/webmachine/rescueable_exception.rb +62 -0
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/resource/callbacks.rb +11 -9
- data/lib/webmachine/response.rb +3 -5
- data/lib/webmachine/spec/IO_response.body +1 -0
- data/lib/webmachine/spec/adapter_lint.rb +83 -37
- data/lib/webmachine/spec/test_resource.rb +15 -4
- data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
- data/lib/webmachine/streaming/io_encoder.rb +7 -1
- 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 +17 -9
- data/spec/webmachine/adapter_spec.rb +14 -15
- data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
- data/spec/webmachine/adapters/rack_mapped_spec.rb +71 -0
- data/spec/webmachine/adapters/rack_spec.rb +32 -6
- data/spec/webmachine/adapters/reel_spec.rb +16 -12
- 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 +49 -43
- data/spec/webmachine/decision/falsey_spec.rb +4 -4
- data/spec/webmachine/decision/flow_spec.rb +195 -145
- data/spec/webmachine/decision/fsm_spec.rb +81 -19
- data/spec/webmachine/decision/helpers_spec.rb +20 -20
- data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +22 -0
- data/spec/webmachine/dispatcher/route_spec.rb +114 -32
- data/spec/webmachine/dispatcher_spec.rb +49 -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 +70 -39
- data/spec/webmachine/rescueable_exception_spec.rb +15 -0
- data/spec/webmachine/resource/authentication_spec.rb +6 -6
- data/spec/webmachine/response_spec.rb +18 -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 +78 -228
- 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
|
@@ -3,6 +3,7 @@ require 'spec_helper'
|
|
|
3
3
|
describe Webmachine::Dispatcher do
|
|
4
4
|
let(:dispatcher) { Webmachine.application.dispatcher }
|
|
5
5
|
let(:request) { Webmachine::Request.new("GET", URI.parse("http://localhost:8080/"), Webmachine::Headers["accept" => "*/*"], "") }
|
|
6
|
+
let(:request2) { Webmachine::Request.new("GET", URI.parse("http://localhost:8080/hello/bob.html"), Webmachine::Headers["accept" => "*/*"], "") }
|
|
6
7
|
let(:response) { Webmachine::Response.new }
|
|
7
8
|
let(:resource) do
|
|
8
9
|
Class.new(Webmachine::Resource) do
|
|
@@ -14,66 +15,90 @@ describe Webmachine::Dispatcher do
|
|
|
14
15
|
def to_html; "goodbye, cruel world"; end
|
|
15
16
|
end
|
|
16
17
|
end
|
|
17
|
-
let(:
|
|
18
|
+
let(:resource3) do
|
|
19
|
+
Class.new(Webmachine::Resource) do
|
|
20
|
+
def to_html
|
|
21
|
+
name, format = request.path_info[:captures]
|
|
22
|
+
"Hello #{name} with #{format}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
let(:fsm){ double }
|
|
18
27
|
|
|
19
28
|
before { dispatcher.reset }
|
|
20
29
|
|
|
21
30
|
it "should add routes from a block" do
|
|
22
31
|
_resource = resource
|
|
23
|
-
Webmachine.routes do
|
|
24
|
-
add [
|
|
25
|
-
end.
|
|
26
|
-
dispatcher.routes.
|
|
32
|
+
expect(Webmachine.routes do
|
|
33
|
+
add [:*], _resource
|
|
34
|
+
end).to eq(Webmachine)
|
|
35
|
+
expect(dispatcher.routes.size).to eq(1)
|
|
27
36
|
end
|
|
28
37
|
|
|
29
38
|
it "should add routes" do
|
|
30
39
|
expect {
|
|
31
|
-
dispatcher.add_route [
|
|
40
|
+
dispatcher.add_route [:*], resource
|
|
32
41
|
}.to_not raise_error
|
|
33
42
|
end
|
|
34
43
|
|
|
35
44
|
it "should have add_route return the newly created route" do
|
|
36
|
-
route = dispatcher.add_route [
|
|
37
|
-
route.
|
|
45
|
+
route = dispatcher.add_route [:*], resource
|
|
46
|
+
expect(route).to be_instance_of Webmachine::Dispatcher::Route
|
|
38
47
|
end
|
|
39
48
|
|
|
40
49
|
it "should route to the proper resource" do
|
|
41
50
|
dispatcher.add_route ["goodbye"], resource2
|
|
42
|
-
dispatcher.add_route [
|
|
43
|
-
Webmachine::Decision::FSM.
|
|
44
|
-
fsm.
|
|
51
|
+
dispatcher.add_route [:*], resource
|
|
52
|
+
expect(Webmachine::Decision::FSM).to receive(:new).with(instance_of(resource), request, response).and_return(fsm)
|
|
53
|
+
expect(fsm).to receive(:run)
|
|
45
54
|
dispatcher.dispatch(request, response)
|
|
46
55
|
end
|
|
56
|
+
it "should handle regex path segments in route definition" do
|
|
57
|
+
dispatcher.add_route ["hello", /(.*)\.(.*)/], resource3
|
|
58
|
+
expect(Webmachine::Decision::FSM).to receive(:new).with(instance_of(resource3), request2, response).and_return(fsm)
|
|
59
|
+
expect(fsm).to receive(:run)
|
|
60
|
+
dispatcher.dispatch(request2, response)
|
|
61
|
+
end
|
|
47
62
|
|
|
48
63
|
it "should apply route to request before creating the resource" do
|
|
49
|
-
route = dispatcher.add_route [
|
|
64
|
+
route = dispatcher.add_route [:*], resource
|
|
50
65
|
applied = false
|
|
51
66
|
|
|
52
|
-
route.
|
|
53
|
-
resource.
|
|
54
|
-
applied.
|
|
67
|
+
expect(route).to receive(:apply) { applied = true }
|
|
68
|
+
expect(resource).to(receive(:new) do
|
|
69
|
+
expect(applied).to be(true)
|
|
55
70
|
resource2.new(request, response)
|
|
56
|
-
end
|
|
71
|
+
end)
|
|
57
72
|
|
|
58
73
|
dispatcher.dispatch(request, response)
|
|
59
74
|
end
|
|
60
75
|
|
|
61
76
|
it "should add routes with guards" do
|
|
62
77
|
dispatcher.add [], lambda {|req| req.method == "POST" }, resource
|
|
63
|
-
dispatcher.add [
|
|
78
|
+
dispatcher.add [:*], resource2 do |req|
|
|
64
79
|
!req.query.empty?
|
|
65
80
|
end
|
|
66
81
|
request.uri.query = "?foo=bar"
|
|
67
|
-
dispatcher.routes.
|
|
68
|
-
Webmachine::Decision::FSM.
|
|
69
|
-
fsm.
|
|
82
|
+
expect(dispatcher.routes.size).to eq(2)
|
|
83
|
+
expect(Webmachine::Decision::FSM).to receive(:new).with(instance_of(resource2), request, response).and_return(fsm)
|
|
84
|
+
expect(fsm).to receive(:run)
|
|
85
|
+
dispatcher.dispatch(request, response)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "should respond with a valid resource for a 404" do
|
|
70
89
|
dispatcher.dispatch(request, response)
|
|
90
|
+
expect(response.code).to eq(404)
|
|
91
|
+
expect(response.body).to_not be_empty
|
|
92
|
+
expect(response.headers).to have_key('Content-Length')
|
|
93
|
+
expect(response.headers).to have_key('Date')
|
|
71
94
|
end
|
|
72
95
|
|
|
73
|
-
it "should respond with valid resource
|
|
96
|
+
it "should respond with a valid resource for a 404 with a custom Accept header" do
|
|
97
|
+
request.headers['Accept'] = "application/json"
|
|
74
98
|
dispatcher.dispatch(request, response)
|
|
75
|
-
response.code.
|
|
76
|
-
response.body.
|
|
77
|
-
response.headers.
|
|
99
|
+
expect(response.code).to eq(404)
|
|
100
|
+
expect(response.body).to_not be_empty
|
|
101
|
+
expect(response.headers).to have_key('Content-Length')
|
|
102
|
+
expect(response.headers).to have_key('Date')
|
|
78
103
|
end
|
|
79
104
|
end
|
|
@@ -6,28 +6,28 @@ describe Webmachine::ETag do
|
|
|
6
6
|
|
|
7
7
|
subject { etag }
|
|
8
8
|
|
|
9
|
-
it {
|
|
10
|
-
it {
|
|
9
|
+
it { is_expected.to eq(etag_str) }
|
|
10
|
+
it { is_expected.to be_kind_of(described_class) }
|
|
11
11
|
its(:to_s) { should == '"deadbeef12345678"' }
|
|
12
12
|
its(:etag) { should == '"deadbeef12345678"' }
|
|
13
|
-
it {
|
|
13
|
+
it { is_expected.to eq(described_class.new(etag_str.dup)) }
|
|
14
14
|
|
|
15
15
|
context "when the original etag is unquoted" do
|
|
16
16
|
let(:etag_str) { 'deadbeef12345678' }
|
|
17
17
|
|
|
18
|
-
it {
|
|
18
|
+
it { is_expected.to eq(etag_str) }
|
|
19
19
|
its(:to_s) { should == '"deadbeef12345678"' }
|
|
20
20
|
its(:etag) { should == '"deadbeef12345678"' }
|
|
21
|
-
it {
|
|
21
|
+
it { is_expected.to eq(described_class.new(etag_str.dup)) }
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
context "when the original etag contains unbalanced quotes" do
|
|
25
25
|
let(:etag_str) { 'deadbeef"12345678' }
|
|
26
26
|
|
|
27
|
-
it {
|
|
27
|
+
it { is_expected.to eq(etag_str) }
|
|
28
28
|
its(:to_s) { should == '"deadbeef\\"12345678"' }
|
|
29
29
|
its(:etag) { should == '"deadbeef\\"12345678"' }
|
|
30
|
-
it {
|
|
30
|
+
it { is_expected.to eq(described_class.new(etag_str.dup)) }
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -37,39 +37,39 @@ describe Webmachine::WeakETag do
|
|
|
37
37
|
|
|
38
38
|
subject { weak_etag }
|
|
39
39
|
|
|
40
|
-
it {
|
|
41
|
-
it {
|
|
40
|
+
it { is_expected.to eq(strong_etag) }
|
|
41
|
+
it { is_expected.to be_kind_of(described_class) }
|
|
42
42
|
its(:to_s) { should == 'W/"deadbeef12345678"' }
|
|
43
43
|
its(:etag) { should == '"deadbeef12345678"' }
|
|
44
|
-
it {
|
|
44
|
+
it { is_expected.to eq(described_class.new(strong_etag.dup)) }
|
|
45
45
|
|
|
46
46
|
context "when the original etag is unquoted" do
|
|
47
47
|
let(:strong_etag) { 'deadbeef12345678' }
|
|
48
48
|
|
|
49
|
-
it {
|
|
50
|
-
it {
|
|
49
|
+
it { is_expected.to eq(strong_etag) }
|
|
50
|
+
it { is_expected.to be_kind_of(described_class) }
|
|
51
51
|
its(:to_s) { should == 'W/"deadbeef12345678"' }
|
|
52
52
|
its(:etag) { should == '"deadbeef12345678"' }
|
|
53
|
-
it {
|
|
53
|
+
it { is_expected.to eq(described_class.new(strong_etag.dup)) }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
context "when the original etag contains unbalanced quotes" do
|
|
57
57
|
let(:strong_etag) { 'deadbeef"12345678' }
|
|
58
58
|
|
|
59
|
-
it {
|
|
60
|
-
it {
|
|
59
|
+
it { is_expected.to eq(strong_etag) }
|
|
60
|
+
it { is_expected.to be_kind_of(described_class) }
|
|
61
61
|
its(:to_s) { should == 'W/"deadbeef\\"12345678"' }
|
|
62
62
|
its(:etag) { should == '"deadbeef\\"12345678"' }
|
|
63
|
-
it {
|
|
63
|
+
it { is_expected.to eq(described_class.new(strong_etag.dup)) }
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
context "when the original etag is already a weak tag" do
|
|
67
67
|
let(:strong_etag) { 'W/"deadbeef12345678"' }
|
|
68
68
|
|
|
69
|
-
it {
|
|
70
|
-
it {
|
|
69
|
+
it { is_expected.to eq(strong_etag) }
|
|
70
|
+
it { is_expected.to be_kind_of(described_class) }
|
|
71
71
|
its(:to_s) { should == 'W/"deadbeef12345678"' }
|
|
72
72
|
its(:etag) { should == '"deadbeef12345678"' }
|
|
73
|
-
it {
|
|
73
|
+
it { is_expected.to eq(described_class.new(strong_etag.dup)) }
|
|
74
74
|
end
|
|
75
75
|
end
|
|
@@ -3,20 +3,20 @@ require 'spec_helper'
|
|
|
3
3
|
describe Webmachine::Events do
|
|
4
4
|
describe ".backend" do
|
|
5
5
|
it "defaults to AS::Notifications" do
|
|
6
|
-
described_class.backend.
|
|
6
|
+
expect(described_class.backend).to be(AS::Notifications)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
describe ".publish" do
|
|
11
11
|
it "calls the backend" do
|
|
12
|
-
described_class.backend.
|
|
12
|
+
expect(described_class.backend).to receive(:publish).with('test.event', 1, 'two')
|
|
13
13
|
described_class.publish('test.event', 1, 'two')
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
describe ".instrument" do
|
|
18
18
|
it "calls the backend" do
|
|
19
|
-
described_class.backend.
|
|
19
|
+
expect(described_class.backend).to receive(:instrument).with(
|
|
20
20
|
'test.event', {}
|
|
21
21
|
).and_yield
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ describe Webmachine::Events do
|
|
|
26
26
|
|
|
27
27
|
describe ".subscribe" do
|
|
28
28
|
it "calls the backend" do
|
|
29
|
-
described_class.backend.
|
|
29
|
+
expect(described_class.backend).to receive(:subscribe).with(
|
|
30
30
|
'test.event'
|
|
31
31
|
).and_yield
|
|
32
32
|
|
|
@@ -38,7 +38,7 @@ describe Webmachine::Events do
|
|
|
38
38
|
it "calls the backend" do
|
|
39
39
|
callback = Proc.new { }
|
|
40
40
|
|
|
41
|
-
described_class.backend.
|
|
41
|
+
expect(described_class.backend).to receive(:subscribed).with(
|
|
42
42
|
callback, 'test.event'
|
|
43
43
|
).and_yield
|
|
44
44
|
|
|
@@ -50,7 +50,7 @@ describe Webmachine::Events do
|
|
|
50
50
|
it "calls the backend" do
|
|
51
51
|
subscriber = described_class.subscribe('test.event') { }
|
|
52
52
|
|
|
53
|
-
described_class.backend.
|
|
53
|
+
expect(described_class.backend).to receive(:unsubscribe).with(subscriber)
|
|
54
54
|
|
|
55
55
|
described_class.unsubscribe(subscriber)
|
|
56
56
|
end
|
|
@@ -3,14 +3,14 @@ require 'spec_helper'
|
|
|
3
3
|
describe Webmachine::Headers do
|
|
4
4
|
it "should set and access values insensitive to case" do
|
|
5
5
|
subject['Content-TYPE'] = "text/plain"
|
|
6
|
-
subject['CONTENT-TYPE'].
|
|
7
|
-
subject.delete('CoNtEnT-tYpE').
|
|
6
|
+
expect(subject['CONTENT-TYPE']).to eq('text/plain')
|
|
7
|
+
expect(subject.delete('CoNtEnT-tYpE')).to eq('text/plain')
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
describe "#from_cgi" do
|
|
11
11
|
it "should understand the Content-Length header" do
|
|
12
12
|
headers = described_class.from_cgi("CONTENT_LENGTH" => 14)
|
|
13
|
-
headers["content-length"].
|
|
13
|
+
expect(headers["content-length"]).to eq(14)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -22,10 +22,10 @@ describe Webmachine::Headers do
|
|
|
22
22
|
'Accept', 'application/json'
|
|
23
23
|
]
|
|
24
24
|
|
|
25
|
-
headers.to_hash.
|
|
25
|
+
expect(headers.to_hash).to eq({
|
|
26
26
|
'content-type' => 'application/json',
|
|
27
27
|
'accept' => 'application/json'
|
|
28
|
-
}
|
|
28
|
+
})
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -38,10 +38,10 @@ describe Webmachine::Headers do
|
|
|
38
38
|
]
|
|
39
39
|
]
|
|
40
40
|
|
|
41
|
-
headers.to_hash.
|
|
41
|
+
expect(headers.to_hash).to eq({
|
|
42
42
|
'content-type' => 'application/json',
|
|
43
43
|
'accept' => 'application/json'
|
|
44
|
-
}
|
|
44
|
+
})
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -52,10 +52,10 @@ describe Webmachine::Headers do
|
|
|
52
52
|
'Accept' => 'application/json'
|
|
53
53
|
]
|
|
54
54
|
|
|
55
|
-
headers.to_hash.
|
|
55
|
+
expect(headers.to_hash).to eq({
|
|
56
56
|
'content-type' => 'application/json',
|
|
57
57
|
'accept' => 'application/json'
|
|
58
|
-
}
|
|
58
|
+
})
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
end
|
|
@@ -64,7 +64,7 @@ describe Webmachine::Headers do
|
|
|
64
64
|
subject { described_class['Content-Type' => 'application/json'] }
|
|
65
65
|
|
|
66
66
|
it "returns the value for the given key" do
|
|
67
|
-
subject.fetch('conTent-tYpe').
|
|
67
|
+
expect(subject.fetch('conTent-tYpe')).to eq('application/json')
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
context "acessing a missing key" do
|
|
@@ -74,13 +74,13 @@ describe Webmachine::Headers do
|
|
|
74
74
|
|
|
75
75
|
context "and a default value given" do
|
|
76
76
|
it "returns the default value if the key does not exist" do
|
|
77
|
-
subject.fetch('accept', 'text/html').
|
|
77
|
+
expect(subject.fetch('accept', 'text/html')).to eq('text/html')
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
context "and a block given" do
|
|
82
82
|
it "passes the value to the block and returns the block's result" do
|
|
83
|
-
subject.fetch('access') {|k| "#{k} not found"}.
|
|
83
|
+
expect(subject.fetch('access') {|k| "#{k} not found"}).to eq('access not found')
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
end
|
|
@@ -89,11 +89,11 @@ describe Webmachine::Headers do
|
|
|
89
89
|
context "filtering with #grep" do
|
|
90
90
|
subject { described_class["content-type" => "text/plain", "etag" => '"abcdef1234567890"'] }
|
|
91
91
|
it "should filter keys by the given pattern" do
|
|
92
|
-
subject.grep(/content/i).
|
|
92
|
+
expect(subject.grep(/content/i)).to include("content-type")
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
it "should return a Headers instance" do
|
|
96
|
-
subject.grep(/etag/i).
|
|
96
|
+
expect(subject.grep(/etag/i)).to be_instance_of(described_class)
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
end
|
|
@@ -5,53 +5,53 @@ describe Webmachine::MediaType do
|
|
|
5
5
|
subject { described_class.new("application/xml", {"charset" => "UTF-8"}) }
|
|
6
6
|
|
|
7
7
|
context "equivalence" do
|
|
8
|
-
it {
|
|
9
|
-
it {
|
|
8
|
+
it { is_expected.to eq(raw_type) }
|
|
9
|
+
it { is_expected.to eq(described_class.parse(raw_type)) }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
context "when it is the wildcard type" do
|
|
13
13
|
subject { described_class.new("*/*") }
|
|
14
|
-
it {
|
|
14
|
+
it { is_expected.to be_matches_all }
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
context "parsing a type" do
|
|
18
18
|
it "should return MediaTypes untouched" do
|
|
19
|
-
described_class.parse(subject).
|
|
19
|
+
expect(described_class.parse(subject)).to equal(subject)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it "should parse a String" do
|
|
23
23
|
type = described_class.parse(raw_type)
|
|
24
|
-
type.
|
|
25
|
-
type.type.
|
|
26
|
-
type.params.
|
|
24
|
+
expect(type).to be_kind_of(described_class)
|
|
25
|
+
expect(type.type).to eq("application/xml")
|
|
26
|
+
expect(type.params).to eq({"charset" => "UTF-8"})
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it "should parse a type/params pair" do
|
|
30
30
|
type = described_class.parse(["application/xml", {"charset" => "UTF-8"}])
|
|
31
|
-
type.
|
|
32
|
-
type.type.
|
|
33
|
-
type.params.
|
|
31
|
+
expect(type).to be_kind_of(described_class)
|
|
32
|
+
expect(type.type).to eq("application/xml")
|
|
33
|
+
expect(type.params).to eq({"charset" => "UTF-8"})
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
it "should parse a type/params pair where the type has some params in the string" do
|
|
37
37
|
type = described_class.parse(["application/xml;version=1", {"charset" => "UTF-8"}])
|
|
38
|
-
type.
|
|
39
|
-
type.type.
|
|
40
|
-
type.params.
|
|
38
|
+
expect(type).to be_kind_of(described_class)
|
|
39
|
+
expect(type.type).to eq("application/xml")
|
|
40
|
+
expect(type.params).to eq({"charset" => "UTF-8", "version" => "1"})
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
it "should parse a type/params pair with params and whitespace in the string" do
|
|
44
44
|
type = described_class.parse(["multipart/form-data; boundary=----------------------------2c46a7bec2b9", {"charset" => "UTF-8"}])
|
|
45
|
-
type.
|
|
46
|
-
type.type.
|
|
47
|
-
type.params.
|
|
45
|
+
expect(type).to be_kind_of(described_class)
|
|
46
|
+
expect(type.type).to eq("multipart/form-data")
|
|
47
|
+
expect(type.params).to eq({"boundary" => "----------------------------2c46a7bec2b9", "charset" => "UTF-8"})
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
it "should parse a type/params pair where type has single-token params" do
|
|
51
51
|
type = described_class.parse(["text/html;q=1;rdfa", {"charset" => "UTF-8"}])
|
|
52
|
-
type.
|
|
53
|
-
type.type.
|
|
54
|
-
type.params.
|
|
52
|
+
expect(type).to be_kind_of(described_class)
|
|
53
|
+
expect(type.type).to eq("text/html")
|
|
54
|
+
expect(type.params).to eq({"q" => "1", "rdfa" => "", "charset" => "UTF-8"})
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
it "should raise an error when given an invalid type/params pair" do
|
|
@@ -62,24 +62,24 @@ describe Webmachine::MediaType do
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
describe "matching a requested type" do
|
|
65
|
-
it {
|
|
66
|
-
it {
|
|
67
|
-
it {
|
|
68
|
-
it {
|
|
69
|
-
it {
|
|
70
|
-
it {
|
|
71
|
-
it {
|
|
65
|
+
it { is_expected.to be_exact_match("application/xml;charset=UTF-8") }
|
|
66
|
+
it { is_expected.to be_exact_match("application/*;charset=UTF-8") }
|
|
67
|
+
it { is_expected.to be_exact_match("*/*;charset=UTF-8") }
|
|
68
|
+
it { is_expected.to be_exact_match("*;charset=UTF-8") }
|
|
69
|
+
it { is_expected.not_to be_exact_match("text/xml") }
|
|
70
|
+
it { is_expected.not_to be_exact_match("application/xml") }
|
|
71
|
+
it { is_expected.not_to be_exact_match("application/xml;version=1") }
|
|
72
72
|
|
|
73
|
-
it {
|
|
74
|
-
it {
|
|
75
|
-
it {
|
|
76
|
-
it {
|
|
77
|
-
it {
|
|
78
|
-
it {
|
|
73
|
+
it { is_expected.to be_type_matches("application/xml") }
|
|
74
|
+
it { is_expected.to be_type_matches("application/*") }
|
|
75
|
+
it { is_expected.to be_type_matches("*/*") }
|
|
76
|
+
it { is_expected.to be_type_matches("*") }
|
|
77
|
+
it { is_expected.not_to be_type_matches("text/xml") }
|
|
78
|
+
it { is_expected.not_to be_type_matches("text/*") }
|
|
79
79
|
|
|
80
|
-
it {
|
|
81
|
-
it {
|
|
82
|
-
it {
|
|
83
|
-
it {
|
|
80
|
+
it { is_expected.to be_params_match({}) }
|
|
81
|
+
it { is_expected.to be_params_match({"charset" => "UTF-8"}) }
|
|
82
|
+
it { is_expected.not_to be_params_match({"charset" => "Windows-1252"}) }
|
|
83
|
+
it { is_expected.not_to be_params_match({"version" => "3"}) }
|
|
84
84
|
end
|
|
85
85
|
end
|