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