Class: Hanami::Router

Inherits:
Object
  • Object
show all
Defined in:
lib/hanami/router.rb,
lib/hanami/router/leaf.rb,
lib/hanami/router/node.rb,
lib/hanami/router/trie.rb,
lib/hanami/router/block.rb,
lib/hanami/router/route.rb,
lib/hanami/router/errors.rb,
lib/hanami/router/params.rb,
lib/hanami/router/prefix.rb,
lib/hanami/router/segment.rb,
lib/hanami/router/version.rb,
lib/hanami/router/redirect.rb,
lib/hanami/router/constants.rb,
lib/hanami/router/inspector.rb,
lib/hanami/router/url_helpers.rb,
lib/hanami/router/globbed_path.rb,
lib/hanami/router/mounted_path.rb,
lib/hanami/router/formatter/csv.rb,
lib/hanami/router/recognized_route.rb,
lib/hanami/router/formatter/human_friendly.rb

Overview

Rack compatible, lightweight and fast HTTP Router.

Since:

  • 0.1.0

Defined Under Namespace

Modules: Formatter Classes: Block, Error, GlobbedPath, Inspector, InvalidRouteExpansionError, Leaf, MissingEndpointError, MissingRouteError, MountedPath, Node, NotRoutableEndpointError, Params, Prefix, RecognizedRoute, Redirect, Route, Segment, Trie, UnknownHTTPStatusCodeError, UrlHelpers

Constant Summary collapse

VERSION =

Returns the hanami-router version.

Returns:

  • (String)

Since:

  • 0.1.0

"2.2.0"
ROUTER_PARSED_BODY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Since:

  • 2.0.0

"router.parsed_body"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url: DEFAULT_BASE_URL, prefix: DEFAULT_PREFIX, resolver: DEFAULT_RESOLVER, not_allowed: NOT_ALLOWED, not_found: NOT_FOUND, block_context: nil, inspector: nil, &blk) ⇒ Hanami::Router

Initialize the router

Examples:

Base usage

require "hanami/router"

Hanami::Router.new do
  get "/", to: ->(*) { [200, {}, ["OK"]] }
end

Parameters:

  • base_url (String) (defaults to: DEFAULT_BASE_URL)

    the base URL where the HTTP application is deployed

  • prefix (String) (defaults to: DEFAULT_PREFIX)

    the relative URL prefix where the HTTP application is deployed

  • resolver (#call(path, to)) (defaults to: DEFAULT_RESOLVER)

    a resolver for route endpoints

  • block_context (Hanami::Router::Block::Context) (defaults to: nil)
  • not_found (#call(env)) (defaults to: NOT_FOUND)

    default handler when route is not matched

  • blk (Proc)

    the route definitions

Since:

  • 0.1.0



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/hanami/router.rb', line 77

def initialize(base_url: DEFAULT_BASE_URL, prefix: DEFAULT_PREFIX, resolver: DEFAULT_RESOLVER, not_allowed: NOT_ALLOWED, not_found: NOT_FOUND, block_context: nil, inspector: nil, &blk) # rubocop:disable Layout/LineLength
  # TODO: verify if Prefix can handle both name and path prefix
  @path_prefix = Prefix.new(prefix)
  @name_prefix = Prefix.new("")
  @url_helpers = UrlHelpers.new(base_url)
  @base_url = base_url
  @resolver = resolver
  @not_allowed = not_allowed
  @not_found = not_found
  @block_context = block_context
  @fixed = {}
  @variable = {}
  @globs_and_mounts = []
  @blk = blk
  @inspector = inspector
  instance_eval(&blk) if blk
end

Instance Attribute Details

#inspectorHanami::Router::Inspector (readonly)

Routes inspector

Returns:

Since:

  • 2.0.0



37
38
39
# File 'lib/hanami/router.rb', line 37

def inspector
  @inspector
end

#url_helpersObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

URL helpers for other Hanami integrations

Since:

  • 2.0.0



30
31
32
# File 'lib/hanami/router.rb', line 30

def url_helpers
  @url_helpers
end

Class Method Details

.define(&blk) ⇒ Proc

Returns the given block as it is.

Examples:

# apps/web/config/routes.rb
Hanami::Router.define do
  get "/", to: ->(*) { ... }
end

Parameters:

  • blk (Proc)

    a set of route definitions

Returns:

  • (Proc)

    the given block

Since:

  • 0.5.0



52
53
54
# File 'lib/hanami/router.rb', line 52

def self.define(&blk)
  blk
end

Instance Method Details

#call(env) ⇒ Array

Resolve the given Rack env to a registered endpoint and invokes it.

Parameters:

  • env (Hash)

    a Rack env

Returns:

  • (Array)

    a finalized Rack env response

Since:

  • 0.1.0



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/hanami/router.rb', line 102

def call(env)
  endpoint, params = lookup(env)

  unless endpoint
    return not_allowed(env) || not_found(env)
  end

  endpoint.call(
    _params(env, params)
  ).to_a
end

#delete(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts DELETE requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



276
277
278
# File 'lib/hanami/router.rb', line 276

def delete(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::DELETE, path, to, as, constraints, &blk)
end

#fixed(env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 2.0.0



613
614
615
# File 'lib/hanami/router.rb', line 613

def fixed(env)
  @fixed.dig(env[::Rack::REQUEST_METHOD], env[::Rack::PATH_INFO])
end

#get(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts GET requests for the given path. It also defines a route to accept HEAD requests.

Examples:

Proc endpoint

require "hanami/router"

Hanami::Router.new do
  get "/", to: ->(*) { [200, {}, ["OK"]] }
end

Block endpoint

require "hanami/router"

Hanami::Router.new do
  get "/" do
    "OK"
  end
end

Named route

require "hanami/router"

router = Hanami::Router.new do
  get "/", to: ->(*) { [200, {}, ["OK"]] }, as: :welcome
end

router.path(:welcome) # => "/"
router.url(:welcome)  # => #<URI::HTTP http://localhost/>

Constraints

require "hanami/router"

Hanami::Router.new do
  get "/users/:id", to: ->(*) { [200, {}, ["OK"]] }, id: /\d+/
end

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



203
204
205
206
# File 'lib/hanami/router.rb', line 203

def get(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::GET, path, to, as, constraints, &blk)
  add_route(::Rack::HEAD, path, to, as, constraints, &blk)
end

#globbed_or_mounted(env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 2.1.0



625
626
627
628
629
630
631
632
# File 'lib/hanami/router.rb', line 625

def globbed_or_mounted(env)
  @globs_and_mounts.each do |path|
    result = path.endpoint_and_params(env)
    return result unless result.empty?
  end

  nil
end

Defines a route that accepts LINK requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



330
331
332
# File 'lib/hanami/router.rb', line 330

def link(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::LINK, path, to, as, constraints, &blk)
end

#mount(app, at:, **constraints) ⇒ Object

Mount a Rack application at the specified path. All the requests starting with the specified path, will be forwarded to the given application.

All the other methods (eg ‘#get`) support callable objects, but they restrict the range of the acceptable HTTP verb. Mounting an application with #mount doesn’t apply this kind of restriction at the router level, but let the application to decide.

Examples:

require "hanami/router"

Hanami::Router.new do
  mount MyRackApp.new, at: "/foo"
end

Parameters:

  • app (#call)

    a class or an object that responds to #call

  • at (String)

    the relative path where to mount the app

  • constraints (Hash)

    a set of constraints for path variables

Since:

  • 0.1.1



424
425
426
427
428
429
430
431
432
# File 'lib/hanami/router.rb', line 424

def mount(app, at:, **constraints)
  path = prefixed_path(at)
  prefix = Segment.fabricate(path, **constraints)

  @globs_and_mounts << MountedPath.new(prefix, @resolver.call(path, app))
  if inspect?
    @inspector.add_route(Route.new(http_method: "*", path: at, to: app, constraints: constraints))
  end
end

#not_allowed(env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 2.0.0



636
637
638
639
640
641
# File 'lib/hanami/router.rb', line 636

def not_allowed(env)
  allowed_http_methods = _not_allowed_fixed(env) || _not_allowed_variable(env)
  return if allowed_http_methods.nil?

  @not_allowed.call(env, allowed_http_methods)
end

#not_found(env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 2.0.0



645
646
647
# File 'lib/hanami/router.rb', line 645

def not_found(env)
  @not_found.call(env)
end

#options(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts OPTIONS requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



312
313
314
# File 'lib/hanami/router.rb', line 312

def options(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::OPTIONS, path, to, as, constraints, &blk)
end

#patch(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts PATCH requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



240
241
242
# File 'lib/hanami/router.rb', line 240

def patch(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::PATCH, path, to, as, constraints, &blk)
end

#path(name, variables = {}) ⇒ String

Generate an relative URL for a specified named route. The additional arguments will be used to compose the relative URL - in

case it has tokens to match - and for compose the query string.

Examples:

require "hanami/router"

router = Hanami::Router.new(base_url: "https://hanamirb.org") do
  get "/login", to: ->(*) { ... }, as: :login
  get "/:name", to: ->(*) { ... }, as: :framework
end

router.path(:login)                          # => "/login"
router.path(:login, return_to: "/dashboard") # => "/login?return_to=%2Fdashboard"
router.path(:framework, name: "router")      # => "/router"

Parameters:

  • name (Symbol)

    the route name

Returns:

  • (String)

Raises:

See Also:

Since:

  • 0.1.0



460
461
462
# File 'lib/hanami/router.rb', line 460

def path(name, variables = {})
  url_helpers.path(name, variables)
end

#post(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts POST requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



222
223
224
# File 'lib/hanami/router.rb', line 222

def post(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::POST, path, to, as, constraints, &blk)
end

#put(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts PUT requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



258
259
260
# File 'lib/hanami/router.rb', line 258

def put(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::PUT, path, to, as, constraints, &blk)
end

#recognize(env, params = {}, options = {}) ⇒ Hanami::Routing::RecognizedRoute

Recognize the given env, path, or name and return a route for testing inspection.

If the route cannot be recognized, it still returns an object for testing inspection.

Examples:

Successful Path Recognition

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize("/books/23")
route.verb      # => "GET" (default)
route.routable? # => true
route.params    # => {:id=>"23"}

Successful Rack Env Recognition

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize(Rack::MockRequest.env_for("/books/23"))
route.verb      # => "GET" (default)
route.routable? # => true
route.params    # => {:id=>"23"}

Successful Named Route Recognition

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize(:book, id: 23)
route.verb      # => "GET" (default)
route.routable? # => true
route.params    # => {:id=>"23"}

Failing Recognition For Unknown Path

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize("/books")
route.verb      # => "GET" (default)
route.routable? # => false

Failing Recognition For Path With Wrong HTTP Verb

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize("/books/23", method: :post)
route.verb      # => "POST"
route.routable? # => false

Failing Recognition For Rack Env With Wrong HTTP Verb

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize(Rack::MockRequest.env_for("/books/23", method: :post))
route.verb      # => "POST"
route.routable? # => false

Failing Recognition Named Route With Wrong Params

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize(:book)
route.verb      # => "GET" (default)
route.routable? # => false

Failing Recognition Named Route With Wrong HTTP Verb

require "hanami/router"

router = Hanami::Router.new do
  get "/books/:id", to: ->(*) { ... }, as: :book
end

route = router.recognize(:book, {method: :post}, {id: 1})
route.verb      # => "POST"
route.routable? # => false
route.params    # => {:id=>"1"}

Parameters:

  • env (Hash, String, Symbol)

    Rack env, path or route name

  • options (Hash) (defaults to: {})

    a set of options for Rack env or route params

  • params (Hash) (defaults to: {})

    a set of params

Returns:

  • (Hanami::Routing::RecognizedRoute)

    the recognized route

See Also:

  • #env_for
  • Hanami::Routing::RecognizedRoute

Since:

  • 0.5.0



602
603
604
605
606
607
608
609
# File 'lib/hanami/router.rb', line 602

def recognize(env, params = {}, options = {})
  require "hanami/router/recognized_route"

  env = env_for(env, params, options)
  endpoint, params = lookup(env)

  RecognizedRoute.new(endpoint, _params(env, params))
end

#redirect(path, to: nil, as: nil, code: DEFAULT_REDIRECT_CODE) ⇒ Object

Defines a route that redirects the incoming request to another path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • code (Integer) (defaults to: DEFAULT_REDIRECT_CODE)

    a HTTP status code to use for the redirect

Raises:

See Also:

Since:

  • 0.1.0



365
366
367
# File 'lib/hanami/router.rb', line 365

def redirect(path, to: nil, as: nil, code: DEFAULT_REDIRECT_CODE)
  get(path, to: _redirect(to, code), as: as)
end

#root(to: nil, &blk) ⇒ Object

Defines a named root route (a GET route for “/”)

Examples:

Proc endpoint

require "hanami/router"

router = Hanami::Router.new do
  root to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
end

Block endpoint

require "hanami/router"

router = Hanami::Router.new do
  root do
    "Hello from Hanami!"
  end
end

URL helpers

require "hanami/router"

router = Hanami::Router.new(base_url: "https://hanamirb.org") do
  root do
    "Hello from Hanami!"
  end
end

router.path(:root) # => "/"
router.url(:root)  # => #<URI::HTTPS https://hanamirb.org>

Parameters:

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.7.0



152
153
154
# File 'lib/hanami/router.rb', line 152

def root(to: nil, &blk)
  get(ROOT_PATH, to: to, as: :root, &blk)
end

#scope(path, &blk) ⇒ Object

Defines a routing scope. Routes defined in the context of a scope, inherit the given path as path prefix and as a named routes prefix.

Examples:

require "hanami/router"

router = Hanami::Router.new do
  scope "v1" do
    get "/users", to: ->(*) { ... }, as: :users
  end
end

router.path(:v1_users) # => "/v1/users"

Parameters:

  • path (String)

    the scope path to be used as a path prefix

  • blk (Proc)

    the routes definitions withing the scope

See Also:

Since:

  • 2.0.0



389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/hanami/router.rb', line 389

def scope(path, &blk)
  path_prefix = @path_prefix
  name_prefix = @name_prefix

  begin
    @path_prefix = @path_prefix.join(path.to_s)
    @name_prefix = @name_prefix.join(path.to_s)
    instance_eval(&blk)
  ensure
    @path_prefix = path_prefix
    @name_prefix = name_prefix
  end
end

#trace(path, to: nil, as: nil, **constraints, &blk) ⇒ Object

Defines a route that accepts TRACE requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



294
295
296
# File 'lib/hanami/router.rb', line 294

def trace(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::TRACE, path, to, as, constraints, &blk)
end

Defines a route that accepts UNLINK requests for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

  • to (#call) (defaults to: nil)

    the Rack endpoint

  • as (Symbol) (defaults to: nil)

    a unique name for the route

  • constraints (Hash)

    a set of constraints for path variables

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

See Also:

Since:

  • 0.1.0



348
349
350
# File 'lib/hanami/router.rb', line 348

def unlink(path, to: nil, as: nil, **constraints, &blk)
  add_route(::Rack::UNLINK, path, to, as, constraints, &blk)
end

#url(name, variables = {}) ⇒ URI::HTTP, URI::HTTPS

Generate an absolute URL for a specified named route. The additional arguments will be used to compose the relative URL - in

case it has tokens to match - and for compose the query string.

Examples:

require "hanami/router"

router = Hanami::Router.new(base_url: "https://hanamirb.org") do
  get "/login", to: ->(*) { ... }, as: :login
  get "/:name", to: ->(*) { ... }, as: :framework
end

router.url(:login)                          # => #<URI::HTTPS https://hanamirb.org/login>
router.url(:login, return_to: "/dashboard") # => #<URI::HTTPS https://hanamirb.org/login?return_to=%2Fdashboard>
router.url(:framework, name: "router")      # => #<URI::HTTPS https://hanamirb.org/router>

Parameters:

  • name (Symbol)

    the route name

Returns:

  • (URI::HTTP, URI::HTTPS)

Raises:

See Also:

Since:

  • 0.1.0



490
491
492
# File 'lib/hanami/router.rb', line 490

def url(name, variables = {})
  url_helpers.url(name, variables)
end

#variable(env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Since:

  • 2.0.0



619
620
621
# File 'lib/hanami/router.rb', line 619

def variable(env)
  @variable[env[::Rack::REQUEST_METHOD]]&.find(env[::Rack::PATH_INFO])
end