class Pendragon::Router
Attributes
@!visibility private
Public Class Methods
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
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
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
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
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
Maps all routes. @return [Array<Pendragon::Route>] flat_map
# File lib/pendragon/router.rb, line 253 def flat_map @flat_map ||= [] end
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
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
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
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
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
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
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
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
@!visibility private
# File lib/pendragon/router.rb, line 260 def append(route) flat_map << route map[route.request_method] << route end
@!visibility private
# File lib/pendragon/router.rb, line 308 def cascade?(response) response && response[1][Constants::Header::CASCADE] == 'pass' end
@!visibility private
# File lib/pendragon/router.rb, line 313 def compile map.each(&method(:on_compile_listener)) @compiled = true end
@!visibility private
# File lib/pendragon/router.rb, line 337 def compiled? @compiled end
@!visibility private
# File lib/pendragon/router.rb, line 275 def error!(error_class, **payload) throw :halt, error_class.new(**payload).to_response end
@!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
@!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
@!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
@!visibility private
# File lib/pendragon/router.rb, line 332 def on_call_listener(env) fail NotImplementedError end
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
@!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
@!visibility private
# File lib/pendragon/router.rb, line 319 def with_optimization compile unless compiled? yield end