webmachine 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 280847c579eab4ad41bfd4ce4d0471cf56a0c7db
4
- data.tar.gz: 521c6386b2202a050ed8dea00b5c3ee2d152a259
3
+ metadata.gz: 76f443d8e22ccb13b371b7aedfd2d02204653d89
4
+ data.tar.gz: d2b04ca2adc316e74903f02696bd55ae6bc1a900
5
5
  SHA512:
6
- metadata.gz: b9e961c7352e6857dddd1f95206d566261d97340f8304fe7f73e021d04a3adf1f47a701d41635bff6c55db65be22561c0ea87f29ddcd6be6751d32a8237d2b20
7
- data.tar.gz: 6f865e4318a96693486f4b454a932d8970f22bd6b008a90a41fe62cc92e55af7e346889026fac8bff3d4c072e2eaa7d9371f4f27dc15171f7cf6a25bc2c3599e
6
+ metadata.gz: ea9cdbfdf582062d7c5eea818816482f99bb75e915625b62263b62936c79b5126c5f046cab13bc72524caf97c1e0cf031551e445b0f10d9d0b0714377e556e7f
7
+ data.tar.gz: 26575bcdc2c324d42c2af0cb1029654a4c4c92003a17738c12036f55908c97d94f8a461e79f24df3700040c480937568a03f55f9c2c21c1dd40ca1fae2cc980a
@@ -1,5 +1,11 @@
1
1
  ### HEAD
2
2
 
3
+ ### 1.4.0 March 20, 2015
4
+
5
+ * Added RackMapped adapter which allows Webmachine apps to be mounted
6
+ at a non-root path using Rack::Map.
7
+ Thanks to [Julian Doherty](https://github.com/madlep) for writing this.
8
+
3
9
  ### 1.3.1 January 15, 2015
4
10
 
5
11
  * Fixed URI construction, including handling IPv6 addresses, when the
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # webmachine for Ruby [![travis](https://travis-ci.org/seancribbs/webmachine-ruby.png?branch=master)](http://travis-ci.org/seancribbs/webmachine-ruby)
1
+ # webmachine for Ruby [![Build Status](https://travis-ci.org/seancribbs/webmachine-ruby.svg)](https://travis-ci.org/seancribbs/webmachine-ruby)
2
2
 
3
3
  webmachine-ruby is a port of
4
4
  [Webmachine](https://github.com/basho/webmachine), which is written in
@@ -10,14 +10,16 @@ run on any webserver that provides a Rack interface. It also lets it run on
10
10
  In order to be compatible with popular deployment stacks,
11
11
  Webmachine has a [Rack](https://github.com/rack/rack) adapter (thanks to Jamis Buck).
12
12
 
13
- Webmachine can be used with Rack middlware features such as Rack::Map and Rack::Cascade as long as:
14
-
15
- 1. The Webmachine app is mounted at the root directory.
16
- 2. Any requests/responses that are handled by the Webmachine app are not modified by the middleware. The behaviours that are encapsulated in Webmachine assume that no modifications
13
+ Webmachine can be used with Rack middlware features such as Rack::Map and Rack::Cascade as long as any requests/responses that are handled by the Webmachine app are **not** modified by the middleware. The behaviours that are encapsulated in Webmachine assume that no modifications
17
14
  are done to requests or response outside of Webmachine.
18
15
 
19
16
  Keep in mind that Webmachine already supports many things that Rack middleware is used for with other HTTP frameworks (eg. etags, specifying supported/preferred Accept and Content-Types).
20
17
 
18
+ The base `Webmachine::Adapters::Rack` class assumes the Webmachine application
19
+ is mounted at the route path `/` (i.e. not using `Rack::Builder#map` or Rails
20
+ `ActionDispatch::Routing::Mapper::Base#mount`). In order to
21
+ map to a subpath, use the `Webmachine::Adapters::RackMapped` adapter instead.
22
+
21
23
  For an example of using Webmachine with Rack middleware, see the [Pact Broker][middleware-example].
22
24
 
23
25
  See the [Rack Adapter API docs][rack-adapter-api-docs] for more information.
@@ -12,9 +12,12 @@ module Webmachine
12
12
  # A minimal "shim" adapter to allow Webmachine to interface with Rack. The
13
13
  # intention here is to allow Webmachine to run under Rack-compatible
14
14
  # web-servers, like unicorn and pow.
15
+ #
15
16
  # The adapter expects your Webmachine application to be mounted at the root path -
16
17
  # it will NOT allow you to nest your Webmachine application at an arbitrary path
17
18
  # eg. map "/api" { run MyWebmachineAPI }
19
+ # To use map your Webmachine application at an arbitrary path, use the
20
+ # `Webmachine::Adapters::RackMapped` subclass instead.
18
21
  #
19
22
  # To use this adapter, create a config.ru file and populate it like so:
20
23
  #
@@ -59,10 +62,7 @@ module Webmachine
59
62
  headers = Webmachine::Headers.from_cgi(env)
60
63
 
61
64
  rack_req = ::Rack::Request.new env
62
- request = Webmachine::Request.new(rack_req.request_method,
63
- rack_req.url,
64
- headers,
65
- RequestBody.new(rack_req))
65
+ request = build_webmachine_request(rack_req, headers)
66
66
 
67
67
  response = Webmachine::Response.new
68
68
  application.dispatcher.dispatch(request, response)
@@ -95,6 +95,26 @@ module Webmachine
95
95
  rack_res.finish
96
96
  end
97
97
 
98
+ protected
99
+ def routing_tokens(rack_req)
100
+ nil # no-op for default, un-mapped rack adapter
101
+ end
102
+
103
+ def base_uri(rack_req)
104
+ nil # no-op for default, un-mapped rack adapter
105
+ end
106
+
107
+ private
108
+ def build_webmachine_request(rack_req, headers)
109
+ Webmachine::Request.new(rack_req.request_method,
110
+ rack_req.url,
111
+ headers,
112
+ RequestBody.new(rack_req),
113
+ routing_tokens(rack_req),
114
+ base_uri(rack_req)
115
+ )
116
+ end
117
+
98
118
  class RackResponse
99
119
  ONE_FIVE = '1.5'.freeze
100
120
 
@@ -166,6 +186,5 @@ module Webmachine
166
186
  end
167
187
  end # class RequestBody
168
188
  end # class Rack
169
-
170
189
  end # module Adapters
171
190
  end # module Webmachine
@@ -0,0 +1,42 @@
1
+ require 'webmachine/adapters/rack'
2
+
3
+ module Webmachine
4
+ module Adapters
5
+ # Provides the same functionality as the parent Webmachine::Adapters::Rack
6
+ # adapter, but allows the Webmachine application to be hosted at an
7
+ # arbitrary path in a parent Rack application (as in Rack `map` or Rails
8
+ # routing `mount`)
9
+ #
10
+ # This functionality is separated out from the parent class to preserve
11
+ # backward compatibility in the behaviour of the parent Rack adpater.
12
+ #
13
+ # To use the adapter in a parent Rack application, map the Webmachine
14
+ # application as follows in a rackup file or Rack::Builder:
15
+ #
16
+ # map '/foo' do
17
+ # run SomeotherRackApp
18
+ #
19
+ # map '/bar' do
20
+ # run MyWebmachineApp.adapter
21
+ # end
22
+ # end
23
+ class RackMapped < Rack
24
+
25
+ protected
26
+
27
+ def routing_tokens(rack_req)
28
+ routing_match = rack_req.path_info.match(Webmachine::Request::ROUTING_PATH_MATCH)
29
+ routing_path = routing_match ? routing_match[1] : ""
30
+ routing_path.split(SLASH)
31
+ end
32
+
33
+ def base_uri(rack_req)
34
+ # rack SCRIPT_NAME env var doesn't end with "/". This causes weird
35
+ # behavour when URI.join concatenates URI components in
36
+ # Webmachine::Decision::Flow#n11
37
+ script_name = rack_req.script_name + SLASH
38
+ URI.join(rack_req.base_url, script_name)
39
+ end
40
+ end # class RackMapped
41
+ end # module Adapters
42
+ end # module Webmachine
@@ -79,13 +79,11 @@ module Webmachine
79
79
  raise ArgumentError, t('not_resource_class', :class => resource.name) unless resource < Resource
80
80
  end
81
81
 
82
- PATH_MATCH = /^\/(.*)/.freeze
83
-
84
82
  # Determines whether the given request matches this route and
85
83
  # should be dispatched to the {#resource}.
86
84
  # @param [Reqeust] request the request object
87
85
  def match?(request)
88
- tokens = request.uri.path.match(PATH_MATCH)[1].split(SLASH)
86
+ tokens = request.routing_tokens
89
87
  bind(tokens, {}) && guards.all? { |guard| guard.call(request) }
90
88
  end
91
89
 
@@ -93,9 +91,9 @@ module Webmachine
93
91
  # route, including path bindings.
94
92
  # @param [Request] request the request object
95
93
  def apply(request)
96
- request.disp_path = request.uri.path.match(PATH_MATCH)[1]
94
+ request.disp_path = request.routing_tokens.join(SLASH)
97
95
  request.path_info = @bindings.dup
98
- tokens = request.disp_path.split(SLASH)
96
+ tokens = request.routing_tokens
99
97
  depth, trailing = bind(tokens, request.path_info)
100
98
  request.path_tokens = trailing || []
101
99
  end
@@ -8,10 +8,11 @@ module Webmachine
8
8
  # should be instantiated by {Adapters} when a request is received
9
9
  class Request
10
10
  HTTP_HEADERS_MATCH = /^(?:[a-z0-9])+(?:_[a-z0-9]+)*$/i.freeze
11
+ ROUTING_PATH_MATCH = /^\/(.*)/.freeze
11
12
 
12
13
  extend Forwardable
13
14
 
14
- attr_reader :method, :uri, :headers, :body
15
+ attr_reader :method, :uri, :headers, :body, :routing_tokens, :base_uri
15
16
  attr_accessor :disp_path, :path_info, :path_tokens
16
17
 
17
18
  # @param [String] method the HTTP request method
@@ -20,9 +21,14 @@ module Webmachine
20
21
  # @param [Headers] headers the HTTP request headers
21
22
  # @param [String,#to_s,#each,nil] body the entity included in the
22
23
  # request, if present
23
- def initialize(method, uri, headers, body)
24
+ def initialize(method, uri, headers, body, routing_tokens=nil, base_uri=nil)
24
25
  @method, @headers, @body = method, headers, body
25
26
  @uri = build_uri(uri, headers)
27
+ @routing_tokens = routing_tokens || @uri.path.match(ROUTING_PATH_MATCH)[1].split(SLASH)
28
+ @base_uri = base_uri || @uri.dup.tap do |u|
29
+ u.path = SLASH
30
+ u.query = nil
31
+ end
26
32
  end
27
33
 
28
34
  def_delegators :headers, :[]
@@ -53,16 +59,6 @@ module Webmachine
53
59
  !(body.nil? || body.empty?)
54
60
  end
55
61
 
56
- # The root URI for the request, ignoring path and query. This is
57
- # useful for calculating relative paths to resources.
58
- # @return [URI]
59
- def base_uri
60
- @base_uri ||= uri.dup.tap do |u|
61
- u.path = SLASH
62
- u.query = nil
63
- end
64
- end
65
-
66
62
  # Returns a hash of query parameters (they come after the ? in the
67
63
  # URI). Note that this does NOT work in the same way as Rails,
68
64
  # i.e. it does not support nested arrays and hashes.
@@ -1,6 +1,6 @@
1
1
  module Webmachine
2
2
  # Library version
3
- VERSION = "1.3.1".freeze
3
+ VERSION = "1.4.0".freeze
4
4
 
5
5
  # String for use in "Server" HTTP response header, which includes
6
6
  # the {VERSION}.
@@ -0,0 +1,71 @@
1
+ require 'webmachine/adapter'
2
+ require 'webmachine/adapters/rack_mapped'
3
+ require 'spec_helper'
4
+ require 'webmachine/spec/adapter_lint'
5
+ require 'rack/test'
6
+
7
+ describe Webmachine::Adapters::RackMapped do
8
+ it_should_behave_like :adapter_lint do
9
+ it "should set Server header" do
10
+ response = client.request(Net::HTTP::Get.new("/test"))
11
+ expect(response["Server"]).to match(/Webmachine/)
12
+ expect(response["Server"]).to match(/Rack/)
13
+ end
14
+ end
15
+ end
16
+
17
+ describe Webmachine::Adapters::RackMapped do
18
+ class CreateResource < Webmachine::Resource
19
+ def allowed_methods
20
+ ["POST"]
21
+ end
22
+
23
+ def content_types_accepted
24
+ [["application/json", :from_json]]
25
+ end
26
+
27
+ def content_types_provided
28
+ [["application/json", :to_json]]
29
+ end
30
+
31
+ def post_is_create?
32
+ true
33
+ end
34
+
35
+ def create_path
36
+ "created_path_here/123"
37
+ end
38
+
39
+ def from_json
40
+ response.body = %{ {"foo": "bar"} }
41
+ end
42
+ end
43
+
44
+ let(:app) do
45
+ Rack::Builder.new do
46
+ map '/some/route' do
47
+ run(Webmachine::Application.new do |app|
48
+ app.add_route(["test"], Test::Resource)
49
+ app.add_route(["create_test"], CreateResource)
50
+ app.configure do | config |
51
+ config.adapter = :RackMapped
52
+ end
53
+ end.adapter)
54
+ end
55
+ end
56
+ end
57
+
58
+ context "using Rack::Test" do
59
+ include Rack::Test::Methods
60
+
61
+ it "provides the full request URI" do
62
+ rack_response = get "some/route/test", nil, {"HTTP_ACCEPT" => "test/response.request_uri"}
63
+ expect(rack_response.body).to eq "http://example.org/some/route/test"
64
+ end
65
+
66
+ it "provides LOCATION header using custom base_uri when creating from POST request" do
67
+ rack_response = post "/some/route/create_test", %{{"foo": "bar"}}, {"HTTP_ACCEPT" => "application/json", "CONTENT_TYPE" => "application/json"}
68
+ expect(rack_response.headers["Location"]).to eq("http://example.org/some/route/created_path_here/123")
69
+ end
70
+ end
71
+ end
@@ -7,7 +7,8 @@ end
7
7
  describe Webmachine::Dispatcher::Route do
8
8
  let(:method) { "GET" }
9
9
  let(:uri) { URI.parse("http://localhost:8080/") }
10
- let(:request){ Webmachine::Request.new(method, uri, Webmachine::Headers.new, "") }
10
+ let(:routing_tokens) { nil }
11
+ let(:request){ Webmachine::Request.new(method, uri, Webmachine::Headers.new, "", routing_tokens) }
11
12
  let(:resource){ Class.new(Webmachine::Resource) }
12
13
 
13
14
  describe '#apply' do
@@ -16,9 +17,7 @@ describe Webmachine::Dispatcher::Route do
16
17
  }
17
18
 
18
19
  describe 'a path_info fragment' do
19
- before do
20
- uri.path = '/hello/planet%20earth%20++'
21
- end
20
+ let(:uri) { URI.parse("http://localhost:8080/hello/planet%20earth%20++") }
22
21
 
23
22
  it 'should decode the value' do
24
23
  route.apply(request)
@@ -30,8 +29,10 @@ describe Webmachine::Dispatcher::Route do
30
29
  matcher :match_route do |*expected|
31
30
  route = Webmachine::Dispatcher::Route.new(expected[0], Class.new(Webmachine::Resource), expected[1] || {})
32
31
  match do |actual|
33
- request.uri.path = actual if String === actual
34
- route.match?(request)
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)
35
36
  end
36
37
 
37
38
  failure_message do |_|
@@ -124,6 +125,18 @@ describe Webmachine::Dispatcher::Route do
124
125
  end
125
126
  end
126
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
127
140
  end
128
141
 
129
142
  context "applying bindings" do
@@ -170,7 +183,8 @@ describe Webmachine::Dispatcher::Route do
170
183
 
171
184
  context "on a deep path" do
172
185
  subject { described_class.new(%w{foo bar baz}, resource) }
173
- before { request.uri.path = "/foo/bar/baz"; subject.apply(request) }
186
+ let(:uri) { URI.parse("http://localhost:8080/foo/bar/baz") }
187
+ before { subject.apply(request) }
174
188
 
175
189
  it "should assign the dispatched path as the path past the initial slash" do
176
190
  expect(request.disp_path).to eq("foo/bar/baz")
@@ -3,11 +3,13 @@ require 'spec_helper'
3
3
  describe Webmachine::Request do
4
4
  subject { request }
5
5
 
6
- let(:uri) { URI.parse("http://localhost:8080/some/resource") }
7
- let(:http_method) { "GET" }
8
- let(:headers) { Webmachine::Headers.new }
9
- let(:body) { "" }
10
- let(:request) { Webmachine::Request.new(http_method, uri, headers, body) }
6
+ let(:uri) { URI.parse("http://localhost:8080/some/resource") }
7
+ let(:http_method) { "GET" }
8
+ let(:headers) { Webmachine::Headers.new }
9
+ let(:body) { "" }
10
+ let(:routing_tokens) { nil }
11
+ let(:base_uri) { nil }
12
+ let(:request) { Webmachine::Request.new(http_method, uri, headers, body, routing_tokens, base_uri) }
11
13
 
12
14
  it "should provide access to the headers via brackets" do
13
15
  subject.headers['Accept'] = "*/*"
@@ -30,8 +32,17 @@ describe Webmachine::Request do
30
32
  expect(subject.content_md5).to be_nil
31
33
  end
32
34
 
33
- it "should calculate a base URI" do
34
- expect(subject.base_uri).to eq(URI.parse("http://localhost:8080/"))
35
+ context "base_uri" do
36
+ it "should calculate a base URI" do
37
+ expect(subject.base_uri).to eq(URI.parse("http://localhost:8080/"))
38
+ end
39
+
40
+ context "when base_uri has been explicitly set" do
41
+ let(:base_uri) { URI.parse("http://localhost:8080/some_base_uri/here") }
42
+ it "should use the provided base_uri" do
43
+ expect(subject.base_uri).to eq(URI.parse("http://localhost:8080/some_base_uri/here"))
44
+ end
45
+ end
35
46
  end
36
47
 
37
48
  it "should provide a hash of query parameters" do
@@ -239,4 +250,24 @@ describe Webmachine::Request do
239
250
  end
240
251
  end
241
252
 
253
+ describe '#routing_tokens' do
254
+ subject { request.routing_tokens }
255
+
256
+ context "haven't been explicitly set" do
257
+ let(:routing_tokens) { nil }
258
+ it "extracts the routing tokens from the path portion of the uri" do
259
+ expect(subject).to eq(["some", "resource"])
260
+ end
261
+ end
262
+
263
+ context "have been explicitly set" do
264
+ let(:routing_tokens) { ["foo", "bar"] }
265
+
266
+ it "uses the specified routing_tokens" do
267
+ expect(subject).to eq(["foo", "bar"])
268
+ end
269
+ end
270
+
271
+ end
272
+
242
273
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Cribbs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-15 00:00:00.000000000 Z
11
+ date: 2015-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -89,6 +89,7 @@ files:
89
89
  - lib/webmachine/adapters/httpkit.rb
90
90
  - lib/webmachine/adapters/lazy_request_body.rb
91
91
  - lib/webmachine/adapters/rack.rb
92
+ - lib/webmachine/adapters/rack_mapped.rb
92
93
  - lib/webmachine/adapters/reel.rb
93
94
  - lib/webmachine/adapters/webrick.rb
94
95
  - lib/webmachine/application.rb
@@ -147,6 +148,7 @@ files:
147
148
  - spec/spec_helper.rb
148
149
  - spec/webmachine/adapter_spec.rb
149
150
  - spec/webmachine/adapters/httpkit_spec.rb
151
+ - spec/webmachine/adapters/rack_mapped_spec.rb
150
152
  - spec/webmachine/adapters/rack_spec.rb
151
153
  - spec/webmachine/adapters/reel_spec.rb
152
154
  - spec/webmachine/adapters/webrick_spec.rb
@@ -202,6 +204,7 @@ test_files:
202
204
  - spec/spec_helper.rb
203
205
  - spec/webmachine/adapter_spec.rb
204
206
  - spec/webmachine/adapters/httpkit_spec.rb
207
+ - spec/webmachine/adapters/rack_mapped_spec.rb
205
208
  - spec/webmachine/adapters/rack_spec.rb
206
209
  - spec/webmachine/adapters/reel_spec.rb
207
210
  - spec/webmachine/adapters/webrick_spec.rb