lib/grape/router.rb in grape-2.0.0 vs lib/grape/router.rb in grape-2.1.0
- old
+ new
@@ -1,10 +1,7 @@
# frozen_string_literal: true
-require 'grape/router/route'
-require 'grape/util/cache'
-
module Grape
class Router
attr_reader :map, :compiled
def self.normalize_path(path)
@@ -13,14 +10,10 @@
path.sub!(%r{/+\Z}, '')
path = '/' if path == ''
path
end
- def self.supported_methods
- @supported_methods ||= Grape::Http::Headers::SUPPORTED_METHODS + ['*']
- end
-
def initialize
@neutral_map = []
@neutral_regexes = []
@map = Hash.new { |hash, key| hash[key] = [] }
@optimized_map = Hash.new { |hash, key| hash[key] = // }
@@ -29,28 +22,29 @@
def compile!
return if compiled
@union = Regexp.union(@neutral_regexes)
@neutral_regexes = nil
- self.class.supported_methods.each do |method|
+ (Grape::Http::Headers::SUPPORTED_METHODS + ['*']).each do |method|
+ next unless map.key?(method)
+
routes = map[method]
- @optimized_map[method] = routes.map.with_index do |route, index|
- route.index = index
- Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
- end
- @optimized_map[method] = Regexp.union(@optimized_map[method])
+ optimized_map = routes.map.with_index { |route, index| route.to_regexp(index) }
+ @optimized_map[method] = Regexp.union(optimized_map)
end
@compiled = true
end
def append(route)
map[route.request_method] << route
end
def associate_routes(pattern, **options)
- @neutral_regexes << Regexp.new("(?<_#{@neutral_map.length}>)#{pattern.to_regexp}")
- @neutral_map << Grape::Router::AttributeTranslator.new(**options, pattern: pattern, index: @neutral_map.length)
+ Grape::Router::GreedyRoute.new(pattern: pattern, **options).then do |greedy_route|
+ @neutral_regexes << greedy_route.to_regexp(@neutral_map.length)
+ @neutral_map << greedy_route
+ end
end
def call(env)
with_optimization do
response, route = identity(env)
@@ -89,30 +83,37 @@
response
end
def transaction(env)
input, method = *extract_input_and_method(env)
- response = yield(input, method)
- return response if response && !(cascade = cascade?(response))
+ # using a Proc is important since `return` will exit the enclosing function
+ cascade_or_return_response = proc do |response|
+ if response
+ cascade?(response).tap do |cascade|
+ return response unless cascade
+ # we need to close the body if possible before dismissing
+ response[2].close if response[2].respond_to?(:close)
+ end
+ end
+ end
+
+ last_response_cascade = cascade_or_return_response.call(yield(input, method))
last_neighbor_route = greedy_match?(input)
# If last_neighbor_route exists and request method is OPTIONS,
# return response by using #call_with_allow_headers.
- return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Grape::Http::Headers::OPTIONS && !cascade
+ return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Rack::OPTIONS && !last_response_cascade
route = match?(input, '*')
- return last_neighbor_route.endpoint.call(env) if last_neighbor_route && cascade && route
+ return last_neighbor_route.endpoint.call(env) if last_neighbor_route && last_response_cascade && route
- if route
- response = process_route(route, env)
- return response if response && !(cascade = cascade?(response))
- end
+ last_response_cascade = cascade_or_return_response.call(process_route(route, env)) if route
- return call_with_allow_headers(env, last_neighbor_route) if !cascade && last_neighbor_route
+ return call_with_allow_headers(env, last_neighbor_route) if !last_response_cascade && last_neighbor_route
nil
end
def process_route(route, env)
@@ -120,16 +121,16 @@
route.exec(env)
end
def make_routing_args(default_args, route, input)
args = default_args || { route_info: route }
- args.merge(route.params(input) || {})
+ args.merge(route.params(input))
end
def extract_input_and_method(env)
- input = string_for(env[Grape::Http::Headers::PATH_INFO])
- method = env[Grape::Http::Headers::REQUEST_METHOD]
+ input = string_for(env[Rack::PATH_INFO])
+ method = env[Rack::REQUEST_METHOD]
[input, method]
end
def with_optimization
compile! unless compiled
@@ -139,21 +140,14 @@
def default_response
[404, { Grape::Http::Headers::X_CASCADE => 'pass' }, ['404 Not Found']]
end
def match?(input, method)
- current_regexp = @optimized_map[method]
- return unless current_regexp.match(input)
-
- last_match = Regexp.last_match
- @map[method].detect { |route| last_match["_#{route.index}"] }
+ @optimized_map[method].match(input) { |m| @map[method].detect { |route| m[route.regexp_capture_index] } }
end
def greedy_match?(input)
- return unless @union.match(input)
-
- last_match = Regexp.last_match
- @neutral_map.detect { |route| last_match["_#{route.index}"] }
+ @union.match(input) { |m| @neutral_map.detect { |route| m[route.regexp_capture_index] } }
end
def call_with_allow_headers(env, route)
prepare_env_from_route(env, route)
env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header.join(', ').freeze