class Pendragon::Router

Attributes

prefix[RW]

@!visibility private

Public Class Methods

new(&block) click to toggle source

Construcsts an instance of router class.

@example construction for router class

require 'pendragon'

Pendragon.new do
  get '/', to: -> { [200, {}, ['hello']] }
end

@yield block a block is evaluated in instance context. @return [Pendragon::Router]

# File lib/pendragon/router.rb, line 70
def initialize(&block)
  @compiled = false
  instance_eval(&block) if block_given?
end
on(event, &listener) click to toggle source

Adds event listener in router class.

@example

require 'pendragon'

class Pendragon::SuperFast < Pendragon::Router
  register :super_fast

  on :call do |env|
    rotation(env) { |route| route.exec(env) }
  end

  on :compile do |method, routes|
    routes.each do |route|
      route.pattern = route.pattern.to_regexp
    end
  end
end

@param [Symbol] event a event name which is :call or :compile

@yieldparam [optional, Hash] env a request environment variables on :call event. @yieldreturn [optional, Array<Integer, Hash, each>, Rack::Response] response

@yieldparam [String] method @yieldparam [Array<Pendragon::Route>] routes

# File lib/pendragon/router.rb, line 55
def self.on(event, &listener)
  define_method('on_%s_listener' % event, &listener)
end
register(name) click to toggle source

Registers new router type onto global maps.

@example registring new router type.

require 'pendragon'

class Pendragon::SuperFast < Pendragon::Router
  register :super_fast
end

Pendragon[:super_fast] #=> Pendragon::SuperFast

@param [Symbol] name a router type identifier @see Pendragon.register

# File lib/pendragon/router.rb, line 25
def self.register(name)
  Pendragon.register(name, self)
end

Public Instance Methods

call(env) click to toggle source

Calls by given env, returns a response conformed Rack style.

@example

require 'pendragon'

router = Pendragon.new do
  get '/', to: -> { [200, {}, ['hello']]  }
end

env = Rack::MockRequest.env_for('/')
router.call(env) #=> [200, {}, ['hello']]

@return [Array<Integer, Hash, each>, Rack::Response] response conformed Rack style

# File lib/pendragon/router.rb, line 109
def call(env)
  catch(:halt) { with_optimization { invoke(env) } }
end
delete(path, to: nil, **options, &block) click to toggle source

Appends a route of DELETE method @see [Pendragon::Router#route]

# File lib/pendragon/router.rb, line 217
def delete(path, to: nil, **options, &block)
  route Constants::Http::DELETE, path, to: to, **options, &block
end
flat_map() click to toggle source

Maps all routes. @return [Array<Pendragon::Route>] flat_map

# File lib/pendragon/router.rb, line 253
def flat_map
  @flat_map ||= []
end
get(path, to: nil, **options, &block) click to toggle source

Appends a route of GET method @see [Pendragon::Router#route]

# File lib/pendragon/router.rb, line 199
def get(path, to: nil, **options, &block)
  route Constants::Http::GET, path, to: to, **options, &block
end
head(path, to: nil, **options, &block) click to toggle source

Appends a route of HEAD method @see [Pendragon::Router#route]

# File lib/pendragon/router.rb, line 223
def head(path, to: nil, **options, &block)
  route Constants::Http::HEAD, path, to: to, **options, &block
end
map() click to toggle source

Maps all routes for each request methods. @return [Hash{String => Array}] map

# File lib/pendragon/router.rb, line 247
def map
  @map ||= Hash.new { |hash, key| hash[key] = [] }
end
namespace(name, &block) click to toggle source

Prefixes a namespace to route path inside given block.

@example

require 'pendragon'

Pendragon.new do
  namespace :foo do
    # This definition is dispatched to '/foo/bar'.
    get '/bar', to: -> { [200, {}, ['hello']] }
  end
end

@yield block a block is evaluated in instance context.

# File lib/pendragon/router.rb, line 88
def namespace(name, &block)
  fail ArgumentError unless block_given?
  (self.prefix ||= []) << name.to_s
  instance_eval(&block)
ensure
  prefix.pop
end
options(path, to: nil, **options, &block) click to toggle source

Appends a route of OPTIONS method @see [Pendragon::Router#route]

# File lib/pendragon/router.rb, line 229
def options(path, to: nil, **options, &block)
  route Constants::Http::OPTIONS, path, to: to, **options, &block
end
post(path, to: nil, **options, &block) click to toggle source

Appends a route of POST method @see [Pendragon::Router#route]

# File lib/pendragon/router.rb, line 205
def post(path, to: nil, **options, &block)
  route Constants::Http::POST, path, to: to, **options, &block
end
put(path, to: nil, **options, &block) click to toggle source

Appends a route of PUT method @see [Pendragon::Router#route]

# File lib/pendragon/router.rb, line 211
def put(path, to: nil, **options, &block)
  route Constants::Http::PUT, path, to: to, **options, &block
end
route(method, path, to: nil, **options, &block) click to toggle source

Appends a new route to router.

@param [String] method A request method, it should be upcased. @param [String] path The application is dispatched to given path. @option [Class, call] :to

# File lib/pendragon/router.rb, line 238
def route(method, path, to: nil, **options, &block)
  app = block_given? ? block : to
  fail ArgumentError, 'Rack application could not be found' unless app
  path = ?/ + prefix.join(?/) + path if prefix && !prefix.empty?
  append Route.new(method: method, pattern: path, application: app, **options)
end

Private Instance Methods

append(route) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 260
def append(route)
  flat_map << route
  map[route.request_method] << route
end
cascade?(response) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 308
def cascade?(response)
  response && response[1][Constants::Header::CASCADE] == 'pass'
end
compile() click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 313
def compile
  map.each(&method(:on_compile_listener))
  @compiled = true
end
compiled?() click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 337
def compiled?
  @compiled
end
error!(error_class, **payload) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 275
def error!(error_class, **payload)
  throw :halt, error_class.new(**payload).to_response
end
extract(env, required: [:input, :method]) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 287
def extract(env, required: [:input, :method])
  extracted = []
  extracted << env[Constants::Env::PATH_INFO] if required.include?(:input)
  extracted << env[Constants::Env::REQUEST_METHOD] if required.include?(:method)
  extracted
end
find_allows(env) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 280
def find_allows(env)
  pattern = env[Constants::Env::PATH_INFO]
  hits = flat_map.select { |route| route.match(pattern) }.map(&:request_method)
  hits.empty? ? nil : hits
end
invoke(env) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 266
def invoke(env)
  response = on_call_listener(env)
  if !response && (allows = find_allows(env))
    error!(Errors::MethodNotAllowed, allows: allows)
  end
  response || error!(Errors::NotFound)
end
on_call_listener(env) click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 332
def on_call_listener(env)
  fail NotImplementedError
end
on_compile_listener(method, routes) click to toggle source

Optional event listener @param [String] method A request method like GET, POST @param [Array<Pendragon::Route>] routes All routes associated to the method @!visibility private

# File lib/pendragon/router.rb, line 328
def on_compile_listener(method, routes)
end
rotation(env, exact_route = nil) { |route| ... } click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 295
def rotation(env, exact_route = nil)
  input, method = extract(env)
  response = nil
  map[method].each do |route|
    next unless route.match(input)
    response = yield(route)
    break(response) unless cascade?(response)
    response = nil
  end
  response
end
with_optimization() { || ... } click to toggle source

@!visibility private

# File lib/pendragon/router.rb, line 319
def with_optimization
  compile unless compiled?
  yield
end