class Grape::Router

Attributes

compiled[R]
map[R]

Public Class Methods

new() click to toggle source
# File lib/grape/router.rb, line 15
def initialize
  @neutral_map = []
  @neutral_regexes = []
  @map = Hash.new { |hash, key| hash[key] = [] }
  @optimized_map = Hash.new { |hash, key| hash[key] = // }
end
normalize_path(path) click to toggle source
# File lib/grape/router.rb, line 7
def self.normalize_path(path)
  path = +"/#{path}"
  path.squeeze!('/')
  path.sub!(%r{/+\Z}, '')
  path = '/' if path == ''
  path
end

Public Instance Methods

append(route) click to toggle source
# File lib/grape/router.rb, line 37
def append(route)
  map[route.request_method] << route
end
associate_routes(pattern, **options) click to toggle source
# File lib/grape/router.rb, line 41
def associate_routes(pattern, **options)
  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
call(env) click to toggle source
# File lib/grape/router.rb, line 48
def call(env)
  with_optimization do
    response, route = identity(env)
    response || rotation(env, route)
  end
end
compile!() click to toggle source
# File lib/grape/router.rb, line 22
def compile!
  return if compiled

  @union = Regexp.union(@neutral_regexes)
  @neutral_regexes = nil
  (Grape::Http::Headers::SUPPORTED_METHODS + ['*']).each do |method|
    next unless map.key?(method)

    routes = 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
recognize_path(input) click to toggle source
# File lib/grape/router.rb, line 55
def recognize_path(input)
  any = with_optimization { greedy_match?(input) }
  return if any == default_response

  any.endpoint
end

Private Instance Methods

call_with_allow_headers(env, route) click to toggle source
# File lib/grape/router.rb, line 153
def call_with_allow_headers(env, route)
  prepare_env_from_route(env, route)
  env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header.join(', ').freeze
  route.endpoint.call(env)
end
cascade?(response) click to toggle source
# File lib/grape/router.rb, line 164
def cascade?(response)
  response && response[1][Grape::Http::Headers::X_CASCADE] == 'pass'
end
default_response() click to toggle source
# File lib/grape/router.rb, line 140
def default_response
  headers = Grape::Util::Header.new.merge(Grape::Http::Headers::X_CASCADE => 'pass')
  [404, headers, ['404 Not Found']]
end
extract_input_and_method(env) click to toggle source
# File lib/grape/router.rb, line 129
def extract_input_and_method(env)
  input = string_for(env[Rack::PATH_INFO])
  method = env[Rack::REQUEST_METHOD]
  [input, method]
end
greedy_match?(input) click to toggle source
# File lib/grape/router.rb, line 149
def greedy_match?(input)
  @union.match(input) { |m| @neutral_map.detect { |route| m[route.regexp_capture_index] } }
end
identity(env) click to toggle source
# File lib/grape/router.rb, line 64
def identity(env)
  route = nil
  response = transaction(env) do |input, method|
    route = match?(input, method)
    process_route(route, env) if route
  end
  [response, route]
end
make_routing_args(default_args, route, input) click to toggle source
# File lib/grape/router.rb, line 124
def make_routing_args(default_args, route, input)
  args = default_args || { route_info: route }
  args.merge(route.params(input))
end
match?(input, method) click to toggle source
# File lib/grape/router.rb, line 145
def match?(input, method)
  @optimized_map[method].match(input) { |m| @map[method].detect { |route| m[route.regexp_capture_index] } }
end
prepare_env_from_route(env, route) click to toggle source
# File lib/grape/router.rb, line 159
def prepare_env_from_route(env, route)
  input, = *extract_input_and_method(env)
  env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(env[Grape::Env::GRAPE_ROUTING_ARGS], route, input)
end
process_route(route, env) click to toggle source
# File lib/grape/router.rb, line 119
def process_route(route, env)
  prepare_env_from_route(env, route)
  route.exec(env)
end
rotation(env, exact_route = nil) click to toggle source
# File lib/grape/router.rb, line 73
def rotation(env, exact_route = nil)
  response = nil
  input, method = *extract_input_and_method(env)
  map[method].each do |route|
    next if exact_route == route
    next unless route.match?(input)

    response = process_route(route, env)
    break unless cascade?(response)
  end
  response
end
string_for(input) click to toggle source
# File lib/grape/router.rb, line 168
def string_for(input)
  self.class.normalize_path(input)
end
transaction(env) { |input, method| ... } click to toggle source
# File lib/grape/router.rb, line 86
def transaction(env)
  input, method = *extract_input_and_method(env)

  # 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 == Rack::OPTIONS && !last_response_cascade

  route = match?(input, '*')

  return last_neighbor_route.endpoint.call(env) if last_neighbor_route && last_response_cascade && route

  last_response_cascade = cascade_or_return_response.call(process_route(route, env)) if route

  return call_with_allow_headers(env, last_neighbor_route) if !last_response_cascade && last_neighbor_route

  nil
end
with_optimization() { ||| default_response| ... } click to toggle source
# File lib/grape/router.rb, line 135
def with_optimization
  compile! unless compiled
  yield || default_response
end