class Grape::Router

Attributes

compiled[R]
map[R]

Public Class Methods

new() click to toggle source
# File lib/grape/router.rb, line 26
def initialize
  @neutral_map = []
  @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 14
def self.normalize_path(path)
  path = "/#{path}"
  path.squeeze!('/')
  path.sub!(%r{/+\Z}, '')
  path = '/' if path == ''
  path
end
supported_methods() click to toggle source
# File lib/grape/router.rb, line 22
def self.supported_methods
  @supported_methods ||= Grape::Http::Headers::SUPPORTED_METHODS + ['*']
end

Public Instance Methods

append(route) click to toggle source
# File lib/grape/router.rb, line 46
def append(route)
  map[route.request_method.to_s.upcase] << route
end
associate_routes(pattern, **options) click to toggle source
# File lib/grape/router.rb, line 50
def associate_routes(pattern, **options)
  regexp = /(?<_#{@neutral_map.length}>)#{pattern.to_regexp}/
  @neutral_map << Any.new(pattern, regexp: regexp, index: @neutral_map.length, **options)
end
call(env) click to toggle source
# File lib/grape/router.rb, line 55
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 32
def compile!
  return if compiled
  @union = Regexp.union(@neutral_map.map(&:regexp))
  self.class.supported_methods.each do |method|
    routes = map[method]
    @optimized_map[method] = routes.map.with_index do |route, index|
      route.index = index
      route.regexp = /(?<_#{index}>#{route.pattern.to_regexp})/
    end
    @optimized_map[method] = Regexp.union(@optimized_map[method])
  end
  @compiled = true
end
recognize_path(input) click to toggle source
# File lib/grape/router.rb, line 62
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, methods, endpoint) click to toggle source
# File lib/grape/router.rb, line 157
def call_with_allow_headers(env, methods, endpoint)
  env[Grape::Env::GRAPE_ALLOWED_METHODS] = methods
  endpoint.call(env)
end
cascade?(response) click to toggle source
# File lib/grape/router.rb, line 162
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
  [404, { Grape::Http::Headers::X_CASCADE => 'pass' }, ['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[Grape::Http::Headers::PATH_INFO])
  method = env[Grape::Http::Headers::REQUEST_METHOD]
  [input, method]
end
greedy_match?(input) click to toggle source
# File lib/grape/router.rb, line 151
def greedy_match?(input)
  return unless @union.match(input)
  last_match = Regexp.last_match
  @neutral_map.detect { |route| last_match["_#{route.index}"] }
end
identity(env) click to toggle source
# File lib/grape/router.rb, line 70
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 144
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}"] }
end
process_route(route, env) click to toggle source
# File lib/grape/router.rb, line 117
def process_route(route, env)
  input, = *extract_input_and_method(env)
  routing_args = env[Grape::Env::GRAPE_ROUTING_ARGS]
  env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(routing_args, route, input)
  route.exec(env)
end
rotation(env, exact_route = nil) click to toggle source
# File lib/grape/router.rb, line 79
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 166
def string_for(input)
  self.class.normalize_path(input)
end
transaction(env) { |input, method| ... } click to toggle source
# File lib/grape/router.rb, line 91
def transaction(env)
  input, method = *extract_input_and_method(env)
  response = yield(input, method)

  return response if response && !(cascade = cascade?(response))
  neighbor = greedy_match?(input)

  # If neighbor exists and request method is OPTIONS,
  # return response by using #call_with_allow_headers.
  return call_with_allow_headers(
    env,
    neighbor.allow_header,
    neighbor.endpoint
  ) if neighbor && method == 'OPTIONS' && !cascade

  route = match?(input, '*')
  return neighbor.endpoint.call(env) if neighbor && cascade && route

  if route
    response = process_route(route, env)
    return response if response && !(cascade = cascade?(response))
  end

  !cascade && neighbor ? call_with_allow_headers(env, neighbor.allow_header, neighbor.endpoint) : 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