Skip to content

Commit 7e9cd6a

Browse files
committed
Store routing tokens on request. Use them for routing match tests
This introduces a `routing_tokens` attribute on `Webmachine::Request`, and uses it to lazily instantiate the tokens used by `Webmachine::Dispatcher::Route#match?`. This is to allow requests to optionally override the tokens generated by splitting the uri path. This is useful if webmachine isn't hosted at the root path of the uri (as when rack map is used). This SHOULD also give a performance increase for request routing, as previously the uri was matched against a regex for every routing test, and then again when the route was applied after it matched. With this approach, the regex is run on the uri once only and then memomized in the request. (note - SHOULD be faster... haven't actually benchmarked it)
1 parent e92f399 commit 7e9cd6a

File tree

4 files changed

+47
-6
lines changed

4 files changed

+47
-6
lines changed

lib/webmachine/dispatcher/route.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,21 @@ def initialize(path_spec, *args)
7979
raise ArgumentError, t('not_resource_class', :class => resource.name) unless resource < Resource
8080
end
8181

82-
PATH_MATCH = /^\/(.*)/.freeze
83-
8482
# Determines whether the given request matches this route and
8583
# should be dispatched to the {#resource}.
8684
# @param [Reqeust] request the request object
8785
def match?(request)
88-
tokens = request.uri.path.match(PATH_MATCH)[1].split(SLASH)
86+
tokens = request.routing_tokens
8987
bind(tokens, {}) && guards.all? { |guard| guard.call(request) }
9088
end
9189

9290
# Decorates the request with information about the dispatch
9391
# route, including path bindings.
9492
# @param [Request] request the request object
9593
def apply(request)
96-
request.disp_path = request.uri.path.match(PATH_MATCH)[1]
94+
request.disp_path = request.routing_tokens.join(SLASH)
9795
request.path_info = @bindings.dup
98-
tokens = request.disp_path.split(SLASH)
96+
tokens = request.routing_tokens
9997
depth, trailing = bind(tokens, request.path_info)
10098
request.path_tokens = trailing || []
10199
end

lib/webmachine/request.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Request
1313

1414
attr_reader :method, :uri, :headers, :body
1515
attr_accessor :disp_path, :path_info, :path_tokens
16+
attr_writer :routing_tokens
1617

1718
# @param [String] method the HTTP request method
1819
# @param [URI] uri the requested URI, including host, scheme and
@@ -164,6 +165,12 @@ def options?
164165
method == OPTIONS_METHOD
165166
end
166167

168+
ROUTING_PATH_MATCH = /^\/(.*)/.freeze
169+
170+
def routing_tokens
171+
@routing_tokens ||= uri.path.match(ROUTING_PATH_MATCH)[1].split(SLASH)
172+
end
173+
167174
private
168175

169176
IPV6_MATCH = /\A\[(?<address> .* )\]:(?<port> \d+ )\z/x.freeze # string like "[::1]:80"

spec/webmachine/dispatcher/route_spec.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ def warn(*msgs); end # silence warnings for tests
3030
matcher :match_route do |*expected|
3131
route = Webmachine::Dispatcher::Route.new(expected[0], Class.new(Webmachine::Resource), expected[1] || {})
3232
match do |actual|
33-
request.uri.path = actual if String === actual
33+
case actual
34+
when String
35+
request.uri.path = actual if String === actual
36+
when Array
37+
request.uri.path = "/some/route/" + actual.join("/")
38+
request.routing_tokens = actual
39+
end
3440
route.match?(request)
3541
end
3642

@@ -124,6 +130,17 @@ def call(request)
124130
end
125131
end
126132
end
133+
134+
context "with a request with explicitly specified routing tokens" do
135+
subject { ["foo", "bar"] }
136+
it { is_expected.to match_route(["foo", "bar"]) }
137+
it { is_expected.to match_route(["foo", :id]) }
138+
it { is_expected.to match_route ['*'] }
139+
it { is_expected.to match_route [:*] }
140+
it { is_expected.not_to match_route(["some", "route", "foo", "bar"]) }
141+
it { is_expected.not_to match_route %w{foo} }
142+
it { is_expected.not_to match_route [:id] }
143+
end
127144
end
128145

129146
context "applying bindings" do

spec/webmachine/request_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,23 @@ def body; block_given? ? yield(@body) : @body; end
239239
end
240240
end
241241

242+
describe '#routing_tokens' do
243+
subject { request.routing_tokens }
244+
245+
context "haven't be explicitly set" do
246+
it "extracts the routing tokens from the path portion of the uri" do
247+
expect(subject).to eq(["some", "resource"])
248+
end
249+
end
250+
251+
context "have been explicitly set" do
252+
before { request.routing_tokens = ["foo", "bar"] }
253+
254+
it "uses the specified routing_tokens" do
255+
expect(subject).to eq(["foo", "bar"])
256+
end
257+
end
258+
259+
end
260+
242261
end

0 commit comments

Comments
 (0)