Class: Hanami::Router

Inherits:
Object
  • Object
show all
Defined in:
gems/gems/hanami-router-1.3.0/lib/hanami/router.rb,
gems/gems/hanami-router-1.3.0/lib/hanami/router/version.rb

Overview

Rack compatible, lightweight and fast HTTP Router.

Examples:

It offers an intuitive DSL, that supports most of the HTTP verbs:

require 'hanami/router'

endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
router = Hanami::Router.new do
  get     '/', to: endpoint # => get and head requests
  post    '/', to: endpoint
  put     '/', to: endpoint
  patch   '/', to: endpoint
  delete  '/', to: endpoint
  options '/', to: endpoint
  trace   '/', to: endpoint
end

Specify an endpoint with :to (Rack compatible object)

require 'hanami/router'

endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
router = Hanami::Router.new do
  get '/', to: endpoint
end

# :to is mandatory for the default resolver (`Hanami::Routing::EndpointResolver.new`),
# This behavior can be changed by passing a custom resolver to `Hanami::Router#initialize`

Specify an endpoint with :to (controller and action string)

require 'hanami/router'

router = Hanami::Router.new do
  get '/', to: 'articles#show' # => Articles::Show
end

# This is a builtin feature for a Hanami::Controller convention.

Specify a named route with :as

require 'hanami/router'

endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org') do
  get '/', to: endpoint, as: :root
end

router.path(:root) # => '/'
router.url(:root)  # => 'https://hanamirb.org/'

# This isn't mandatory for the default route class (`Hanami::Routing::Route`),
# This behavior can be changed by passing a custom route to `Hanami::Router#initialize`

Mount an application

require 'hanami/router'

router = Hanami::Router.new do
  mount Api::App, at: '/api'
end

# All the requests starting with "/api" will be forwarded to Api::App

Since:

  • 0.1.0

Defined Under Namespace

Classes: NotRoutableEndpointError

Constant Summary

VERSION =

Since:

  • 0.1.0

'1.3.0'.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &blk) ⇒ Hanami::Router

Initialize the router.

Examples:

Basic example

require 'hanami/router'

endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }

router = Hanami::Router.new
router.get '/', to: endpoint

# or

router = Hanami::Router.new do
  get '/', to: endpoint
end

Body parsers

require 'json'
require 'hanami/router'

# It parses JSON body and makes the attributes available to the params

endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }

router = Hanami::Router.new(parsers: [:json]) do
  patch '/books/:id', to: endpoint
end

# From the shell

curl http://localhost:2300/books/1    \
  -H "Content-Type: application/json" \
  -H "Accept: application/json"       \
  -d '{"published":"true"}'           \
  -X PATCH

# It returns

[200, {}, ["{:published=>\"true\",:id=>\"1\"}"]]

Custom body parser

require 'hanami/router'

class XmlParser
  def mime_types
    ['application/xml', 'text/xml']
  end

  # Parse body and return a Hash
  def parse(body)
    # ...
  end
end

# It parses XML body and makes the attributes available to the params

endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }

router = Hanami::Router.new(parsers: [XmlParser.new]) do
  patch '/authors/:id', to: endpoint
end

# From the shell

curl http://localhost:2300/authors/1 \
  -H "Content-Type: application/xml" \
  -H "Accept: application/xml"       \
  -d '<name>LG</name>'               \
  -X PATCH

# It returns

[200, {}, ["{:name=>\"LG\",:id=>\"1\"}"]]

Parameters:

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

    the options to initialize the router

  • blk (Proc)

    the optional block to define the routes

Options Hash (options):

  • :scheme (String)

    The HTTP scheme (defaults to "http")

  • :host (String)

    The URL host (defaults to "localhost")

  • :port (String)

    The URL port (defaults to "80")

  • :resolver (Object, #resolve, #find, #action_separator)

    the route resolver (defaults to Hanami::Routing::EndpointResolver.new)

  • :route (Object, #generate)

    the route class (defaults to Hanami::Routing::Route)

  • :action_separator (String)

    the separator between controller and action name (eg. 'dashboard#show', where '#' is the :action_separator)

  • :parsers (Array<Symbol,String,Object #mime_types, parse>)

    the body parsers for mime types

Since:

  • 0.1.0

def initialize(options = {}, &blk)
  @router = Routing::HttpRouter.new(options)
  define(&blk)
end

Class Method Details

.define(&blk) ⇒ Proc

Returns the given block as it is.

When Hanami::Router is used as a standalone gem and the routes are defined into a configuration file, some systems could raise an exception.

Imagine the following file into a Ruby on Rails application:

get '/', to: 'api#index'

Because Ruby on Rails in production mode use to eager load code and the routes file uses top level method calls, it crashes the application.

If we wrap these routes with Hanami::Router.define, the block doesn't get yielded but just returned to the caller as it is.

Usually the receiver of this block is Hanami::Router#initialize, which finally evaluates the block.

Examples:

# apps/web/config/routes.rb
Hanami::Router.define do
  get '/', to: 'home#index'
end

Parameters:

  • blk (Proc)

    a set of route definitions

Returns:

  • (Proc)

    the given block

Since:

  • 0.5.0

def self.define(&blk)
  blk
end

Instance Method Details

#call(env) ⇒ Rack::Response, Array

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

Parameters:

  • env (Hash)

    a Rack env instance

Returns:

  • (Rack::Response, Array)

Since:

  • 0.1.0

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

#define(&blk) ⇒ Hanami::Routing::Route

To support defining routes in the define wrapper.

Examples:

In Hanami framework

class Application < Hanami::Application
  configure do
    routes 'config/routes'
  end
end

# In `config/routes`

define do
  get # ...
end

Parameters:

  • blk (Proc)

    the block to define the routes

Returns:

  • (Hanami::Routing::Route)

Since:

  • 0.2.0

def define(&blk)
  instance_eval(&blk) if block_given?
end

#delete(path, options = {}, &blk) ⇒ Hanami::Routing::Route

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.1.0

def delete(path, options = {}, &blk)
  @router.delete(path, options, &blk)
end

#get(path, options = {}, &blk) ⇒ Hanami::Routing::Route

Defines a route that accepts a GET request for the given path.

Examples:

Fixed matching string

require 'hanami/router'

router = Hanami::Router.new
router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }

String matching with variables

require 'hanami/router'

router = Hanami::Router.new
router.get '/flowers/:id',
  to: ->(env) {
    [
      200,
      {},
      ["Hello from Flower no. #{ env['router.params'][:id] }!"]
    ]
  }

Variables Constraints

require 'hanami/router'

router = Hanami::Router.new
router.get '/flowers/:id',
  id: /\d+/,
  to: ->(env) { [200, {}, [":id must be a number!"]] }

String matching with globbling

require 'hanami/router'

router = Hanami::Router.new
router.get '/*',
  to: ->(env) {
    [
      200,
      {},
      ["This is catch all: #{ env['router.params'].inspect }!"]
    ]
  }

String matching with optional tokens

require 'hanami/router'

router = Hanami::Router.new
router.get '/hanami(.:format)',
  to: ->(env) {
    [200, {}, ["You've requested #{ env['router.params'][:format] }!"]]
  }

Named routes

require 'hanami/router'

router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
router.get '/hanami',
  to: ->(env) { [200, {}, ['Hello from Hanami!']] },
  as: :hanami

router.path(:hanami) # => "/hanami"
router.url(:hanami)  # => "https://hanamirb.org/hanami"

Duck typed endpoints (Rack compatible objects)

require 'hanami/router'

router = Hanami::Router.new

router.get '/hanami',      to: ->(env) { [200, {}, ['Hello from Hanami!']] }
router.get '/middleware', to: Middleware
router.get '/rack-app',   to: RackApp.new
router.get '/method',     to: ActionControllerSubclass.action(:new)

# Everything that responds to #call is invoked as it is

Duck typed endpoints (strings)

require 'hanami/router'

class RackApp
  def call(env)
    # ...
  end
end

router = Hanami::Router.new
router.get '/hanami', to: 'rack_app' # it will map to RackApp.new

Duck typed endpoints (string: controller + action)

require 'hanami/router'

module Flowers
  class Index
    def call(env)
      # ...
    end
  end
end

 router = Hanami::Router.new
 router.get '/flowers', to: 'flowers#index'

 # It will map to Flowers::Index.new, which is the
 # Hanami::Controller convention.

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

Since:

  • 0.1.0

def get(path, options = {}, &blk)
  @router.get(path, options, &blk)
end

#inspectorObject

Returns an routes inspector

Examples:

require 'hanami/router'

router = Hanami::Router.new do
  get    '/',       to: 'home#index'
  get    '/login',  to: 'sessions#new',     as: :login
  post   '/login',  to: 'sessions#create'
  delete '/logout', to: 'sessions#destroy', as: :logout
end

puts router.inspector
  # =>        GET, HEAD  /                        Home::Index
       login  GET, HEAD  /login                   Sessions::New
              POST       /login                   Sessions::Create
       logout GET, HEAD  /logout                  Sessions::Destroy

See Also:

  • Hanami::Routing::RoutesInspector

Since:

  • 0.2.0

def inspector
  require 'hanami/routing/routes_inspector'
  Routing::RoutesInspector.new(@router.routes, @router.prefix)
end

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.8.0

def link(path, options = {}, &blk)
  @router.link(path, options, &blk)
end

#mount(app, options) ⇒ 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:

Basic usage

require 'hanami/router'

Hanami::Router.new do
  mount Api::App.new, at: '/api'
end

# Requests:
#
# GET  /api          # => 200
# GET  /api/articles # => 200
# POST /api/articles # => 200
# GET  /api/unknown  # => 404

Difference between #get and #mount

require 'hanami/router'

Hanami::Router.new do
  get '/rack1',      to: RackOne.new
  mount RackTwo.new, at: '/rack2'
end

# Requests:
#
# # /rack1 will only accept GET
# GET  /rack1        # => 200 (RackOne.new)
# POST /rack1        # => 405
#
# # /rack2 accepts all the verbs and delegate the decision to RackTwo
# GET  /rack2        # => 200 (RackTwo.new)
# POST /rack2        # => 200 (RackTwo.new)

Types of mountable applications

require 'hanami/router'

class RackOne
  def self.call(env)
  end
end

class RackTwo
  def call(env)
  end
end

class RackThree
  def call(env)
  end
end

module Dashboard
  class Index
    def call(env)
    end
  end
end

Hanami::Router.new do
  mount RackOne,                             at: '/rack1'
  mount RackTwo,                             at: '/rack2'
  mount RackThree.new,                       at: '/rack3'
  mount ->(env) {[200, {}, ['Rack Four']]},  at: '/rack4'
  mount 'dashboard#index',                   at: '/dashboard'
end

# 1. RackOne is used as it is (class), because it respond to .call
# 2. RackTwo is initialized, because it respond to #call
# 3. RackThree is used as it is (object), because it respond to #call
# 4. That Proc is used as it is, because it respond to #call
# 5. That string is resolved as Dashboard::Index (Hanami::Controller)

Parameters:

  • app (#call)

    a class or an object that responds to #call

  • options (Hash)

    the options to customize the mount

Options Hash (options):

  • the (:at)

    relative path where to mount the app

Since:

  • 0.1.1

def mount(app, options)
  @router.mount(app, options)
end

#namespace(namespace, &blk) ⇒ Hanami::Routing::Namespace

Defines a Ruby block: all the routes defined within it will be namespaced with the given relative path.

Namespaces blocks can be nested multiple times.

Examples:

Basic example

require 'hanami/router'

Hanami::Router.new do
  namespace 'trees' do
    get '/sequoia', to: endpoint # => '/trees/sequoia'
  end
end

Nested namespaces

require 'hanami/router'

Hanami::Router.new do
  namespace 'animals' do
    namespace 'mammals' do
      get '/cats', to: endpoint # => '/animals/mammals/cats'
    end
  end
end
require 'hanami/router'

router = Hanami::Router.new
router.namespace 'trees' do
  get '/sequoia', to: endpoint # => '/trees/sequoia'
end

Parameters:

  • namespace (String)

    the relative path where the nested routes will be mounted

  • blk (Proc)

    the block that defines the resources

Returns:

  • (Hanami::Routing::Namespace)

    the generated namespace.

See Also:

Since:

  • 0.1.0

def namespace(namespace, &blk)
  Routing::Namespace.new(self, namespace, &blk)
end

#options(path, options = {}, &blk) ⇒ Hanami::Routing::Route

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.1.0

def options(path, options = {}, &blk)
  @router.options(path, options, &blk)
end

#patch(path, options = {}, &blk) ⇒ Hanami::Routing::Route

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.1.0

def patch(path, options = {}, &blk)
  @router.patch(path, options, &blk)
end

#path(route, *args) ⇒ 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(scheme: 'https', host: 'hanamirb.org')
router.get '/login', to: 'sessions#new',    as: :login
router.get '/:name', to: 'frameworks#show', as: :framework

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

Parameters:

  • route (Symbol)

    the route name

Returns:

  • (String)

Raises:

  • (Hanami::Routing::InvalidRouteException)

    when the router fails to recognize a route, because of the given arguments.

Since:

  • 0.1.0

def path(route, *args)
  @router.path(route, *args)
end

#post(path, options = {}, &blk) ⇒ Hanami::Routing::Route

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.1.0

def post(path, options = {}, &blk)
  @router.post(path, options, &blk)
end

#put(path, options = {}, &blk) ⇒ Hanami::Routing::Route

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.1.0

def put(path, options = {}, &blk)
  @router.put(path, options, &blk)
end

#recognize(env, options = {}, params = nil) ⇒ 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: 'books#show', 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: 'books#show', 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: 'books#show', 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: 'books#show', 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: 'books#show', 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: 'books#show', 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: 'books#show', 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: 'books#show', 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: nil)

    a set of params

Returns:

  • (Hanami::Routing::RecognizedRoute)

    the recognized route

See Also:

  • #env_for
  • Hanami::Routing::RecognizedRoute

Since:

  • 0.5.0

def recognize(env, options = {}, params = nil)
  require 'hanami/routing/recognized_route'

  env          = env_for(env, options, params)
  responses, _ = *@router.recognize(env)

  Routing::RecognizedRoute.new(
    responses.nil? ? responses : responses.first,
    env, @router)
end

#redirect(path, options = {}, &endpoint) ⇒ Hanami::Routing::Route

Defines an HTTP redirect

Examples:

require 'hanami/router'

Hanami::Router.new do
  redirect '/legacy',  to: '/new_endpoint'
  redirect '/legacy2', to: '/new_endpoint2', code: 302
end
require 'hanami/router'

router = Hanami::Router.new
router.redirect '/legacy',  to: '/new_endpoint'

Parameters:

  • path (String)

    the path that needs to be redirected

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

    the options to customize the redirect behavior

Options Hash (options):

  • the (Fixnum)

    HTTP status to return (defaults to 301)

Returns:

  • (Hanami::Routing::Route)

    the generated route. This may vary according to the :route option passed to the initializer

See Also:

Since:

  • 0.1.0

def redirect(path, options = {}, &endpoint)
  destination_path = @router.find(options)
  get(path).redirect(destination_path, options[:code] || 301).tap do |route|
    route.dest = Hanami::Routing::RedirectEndpoint.new(destination_path, route.dest)
  end
end

#resource(name, options = {}, &blk) ⇒ Hanami::Routing::Resource

Defines a set of named routes for a single RESTful resource. It has a built-in integration for Hanami::Controller.

Examples:

Default usage

require 'hanami/router'

Hanami::Router.new do
  resource 'identity'
end

# It generates:
#
# +--------+----------------+-------------------+----------+----------------+
# | Verb   | Path           | Action            | Name     | Named Route    |
# +--------+----------------+-------------------+----------+----------------+
# | GET    | /identity      | Identity::Show    | :show    | :identity      |
# | GET    | /identity/new  | Identity::New     | :new     | :new_identity  |
# | POST   | /identity      | Identity::Create  | :create  | :identity      |
# | GET    | /identity/edit | Identity::Edit    | :edit    | :edit_identity |
# | PATCH  | /identity      | Identity::Update  | :update  | :identity      |
# | DELETE | /identity      | Identity::Destroy | :destroy | :identity      |
# +--------+----------------+-------------------+----------+----------------+

Limit the generated routes with :only

require 'hanami/router'

Hanami::Router.new do
  resource 'identity', only: [:show, :new, :create]
end

# It generates:
#
# +--------+----------------+------------------+----------+----------------+
# | Verb   | Path           | Action           | Name     | Named Route    |
# +--------+----------------+------------------+----------+----------------+
# | GET    | /identity      | Identity::Show   | :show    | :identity      |
# | GET    | /identity/new  | Identity::New    | :new     | :new_identity  |
# | POST   | /identity      | Identity::Create | :create  | :identity      |
# +--------+----------------+------------------+----------+----------------+

Limit the generated routes with :except

require 'hanami/router'

Hanami::Router.new do
  resource 'identity', except: [:edit, :update, :destroy]
end

# It generates:
#
# +--------+----------------+------------------+----------+----------------+
# | Verb   | Path           | Action           | Name     | Named Route    |
# +--------+----------------+------------------+----------+----------------+
# | GET    | /identity      | Identity::Show   | :show    | :identity      |
# | GET    | /identity/new  | Identity::New    | :new     | :new_identity  |
# | POST   | /identity      | Identity::Create | :create  | :identity      |
# +--------+----------------+------------------+----------+----------------+

Additional single routes

require 'hanami/router'

Hanami::Router.new do
  resource 'identity', only: [] do
    member do
      patch 'activate'
    end
  end
end

# It generates:
#
# +--------+--------------------+--------------------+------+--------------------+
# | Verb   | Path               | Action             | Name | Named Route        |
# +--------+--------------------+--------------------+------+--------------------+
# | PATCH  | /identity/activate | Identity::Activate |      | :activate_identity |
# +--------+--------------------+--------------------+------+--------------------+

Additional collection routes

require 'hanami/router'

Hanami::Router.new do
  resource 'identity', only: [] do
    collection do
      get 'keys'
    end
  end
end

# It generates:
#
# +------+----------------+----------------+------+----------------+
# | Verb | Path           | Action         | Name | Named Route    |
# +------+----------------+----------------+------+----------------+
# | GET  | /identity/keys | Identity::Keys |      | :keys_identity |
# +------+----------------+----------------+------+----------------+

Parameters:

  • name (String)

    the name of the resource

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

    a set of options to customize the routes

  • blk (Proc)

    a block of code to generate additional routes

Options Hash (options):

  • :only (Array<Symbol>)

    a subset of the default routes that we want to generate

  • :except (Array<Symbol>)

    prevent the given routes to be generated

Returns:

  • (Hanami::Routing::Resource)

See Also:

  • Hanami::Routing::Resource
  • Hanami::Routing::Resource::Action
  • Hanami::Routing::Resource::Options

Since:

  • 0.1.0

def resource(name, options = {}, &blk)
  Routing::Resource.new(self, name, options.merge(separator: @router.action_separator), &blk)
end

#resources(name, options = {}, &blk) ⇒ Hanami::Routing::Resources

Defines a set of named routes for a plural RESTful resource. It has a built-in integration for Hanami::Controller.

Examples:

Default usage

require 'hanami/router'

Hanami::Router.new do
  resources 'articles'
end

# It generates:
#
# +--------+--------------------+-------------------+----------+----------------+
# | Verb   | Path               | Action            | Name     | Named Route    |
# +--------+--------------------+-------------------+----------+----------------+
# | GET    | /articles          | Articles::Index   | :index   | :articles      |
# | GET    | /articles/:id      | Articles::Show    | :show    | :articles      |
# | GET    | /articles/new      | Articles::New     | :new     | :new_articles  |
# | POST   | /articles          | Articles::Create  | :create  | :articles      |
# | GET    | /articles/:id/edit | Articles::Edit    | :edit    | :edit_articles |
# | PATCH  | /articles/:id      | Articles::Update  | :update  | :articles      |
# | DELETE | /articles/:id      | Articles::Destroy | :destroy | :articles      |
# +--------+--------------------+-------------------+----------+----------------+

Limit the generated routes with :only

require 'hanami/router'

Hanami::Router.new do
  resources 'articles', only: [:index]
end

# It generates:
#
# +------+-----------+-----------------+--------+-------------+
# | Verb | Path      | Action          | Name   | Named Route |
# +------+-----------+-----------------+--------+-------------+
# | GET  | /articles | Articles::Index | :index | :articles   |
# +------+-----------+-----------------+--------+-------------+

Limit the generated routes with :except

require 'hanami/router'

Hanami::Router.new do
  resources 'articles', except: [:edit, :update]
end

# It generates:
#
# +--------+--------------------+-------------------+----------+----------------+
# | Verb   | Path               | Action            | Name     | Named Route    |
# +--------+--------------------+-------------------+----------+----------------+
# | GET    | /articles          | Articles::Index   | :index   | :articles      |
# | GET    | /articles/:id      | Articles::Show    | :show    | :articles      |
# | GET    | /articles/new      | Articles::New     | :new     | :new_articles  |
# | POST   | /articles          | Articles::Create  | :create  | :articles      |
# | DELETE | /articles/:id      | Articles::Destroy | :destroy | :articles      |
# +--------+--------------------+-------------------+----------+----------------+

Additional single routes

require 'hanami/router'

Hanami::Router.new do
  resources 'articles', only: [] do
    member do
      patch 'publish'
    end
  end
end

# It generates:
#
# +--------+-----------------------+-------------------+------+-------------------+
# | Verb   | Path                  | Action            | Name | Named Route       |
# +--------+-----------------------+-------------------+------+-------------------+
# | PATCH  | /articles/:id/publish | Articles::Publish |      | :publish_articles |
# +--------+-----------------------+-------------------+------+-------------------+

Additional collection routes

require 'hanami/router'

Hanami::Router.new do
  resources 'articles', only: [] do
    collection do
      get 'search'
    end
  end
end

# It generates:
#
# +------+------------------+------------------+------+------------------+
# | Verb | Path             | Action           | Name | Named Route      |
# +------+------------------+------------------+------+------------------+
# | GET  | /articles/search | Articles::Search |      | :search_articles |
# +------+------------------+------------------+------+------------------+

Parameters:

  • name (String)

    the name of the resource

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

    a set of options to customize the routes

  • blk (Proc)

    a block of code to generate additional routes

Options Hash (options):

  • :only (Array<Symbol>)

    a subset of the default routes that we want to generate

  • :except (Array<Symbol>)

    prevent the given routes to be generated

Returns:

  • (Hanami::Routing::Resources)

See Also:

  • Hanami::Routing::Resources
  • Hanami::Routing::Resources::Action
  • Hanami::Routing::Resource::Options

Since:

  • 0.1.0

def resources(name, options = {}, &blk)
  Routing::Resources.new(self, name, options.merge(separator: @router.action_separator), &blk)
end

#root(options = {}, &blk) ⇒ Hanami::Routing::Route

Defines a root route (a GET route for '/')

Examples:

Fixed matching string

require 'hanami/router'

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

Included names as root (for path and url helpers)

require 'hanami/router'

router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
router.root to: ->(env) { [200, {}, ['Hello from Hanami!']] }

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

Parameters:

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

Since:

  • 0.7.0

def root(options = {}, &blk)
  @router.get(ROOT_PATH, options.merge(as: :root), &blk)
end

#trace(path, options = {}, &blk) ⇒ Hanami::Routing::Route

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

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.1.0

def trace(path, options = {}, &blk)
  @router.trace(path, options, &blk)
end

Defines a route that accepts an UNLINK request for the given path.

Parameters:

  • path (String)

    the relative URL to be matched

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

    the options to customize the route

  • blk (Proc)

    the anonymous proc to be used as endpoint for the route

Options Hash (options):

  • :to (String, Proc, Class, Object#call)

    the endpoint

Returns:

  • (Hanami::Routing::Route)

    this may vary according to the :route option passed to the constructor

See Also:

Since:

  • 0.8.0

def unlink(path, options = {}, &blk)
  @router.unlink(path, options, &blk)
end

#url(route, *args) ⇒ String

Generate a 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(scheme: 'https', host: 'hanamirb.org')
router.get '/login', to: 'sessions#new', as: :login
router.get '/:name', to: 'frameworks#show', as: :framework

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

Parameters:

  • route (Symbol)

    the route name

Returns:

  • (String)

Raises:

  • (Hanami::Routing::InvalidRouteException)

    when the router fails to recognize a route, because of the given arguments.

Since:

  • 0.1.0

def url(route, *args)
  @router.url(route, *args)
end