class Hanami::Router

Rack compatible, lightweight and fast HTTP Router.

@since 0.1.0

@example 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

@example 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`

@example 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.

@example 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`

@example 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

Constants

ROOT_PATH

Defines root path

@since 0.7.0 @api private

@see Hanami::Router#root

VERSION

@since 0.1.0

Public Class Methods

define(&blk) click to toggle source

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.

@param blk [Proc] a set of route definitions

@return [Proc] the given block

@since 0.5.0

@example

# apps/web/config/routes.rb
Hanami::Router.define do
  get '/', to: 'home#index'
end
# File lib/hanami/router.rb, line 140
def self.define(&blk)
  blk
end
new(options = {}, &blk) click to toggle source

Initialize the router.

@param options [Hash] the options to initialize the router

@option options [String] :scheme The HTTP scheme (defaults to `“http”`) @option options [String] :host The URL host (defaults to `“localhost”`) @option options [String] :port The URL port (defaults to `“80”`) @option options [Object, resolve, find, action_separator] :resolver

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

@option options [Object, generate] :route the route class

(defaults to `Hanami::Routing::Route`)

@option options [String] :action_separator the separator between controller

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

@option options [Array<Symbol,String,Object mime_types, parse>] :parsers

the body parsers for mime types

@param blk [Proc] the optional block to define the routes

@return [Hanami::Router] self

@since 0.1.0

@example 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

@example 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\"}"]]

@example 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\"}"]]
# File lib/hanami/router.rb, line 237
def initialize(options = {}, &blk)
  @router = Routing::HttpRouter.new(options)
  define(&blk)
end

Public Instance Methods

call(env) click to toggle source

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

@param env [Hash] a Rack env instance

@return [Rack::Response, Array]

@since 0.1.0

# File lib/hanami/router.rb, line 1018
def call(env)
  @router.call(env)
end
define(&blk) click to toggle source

To support defining routes in the `define` wrapper.

@param blk [Proc] the block to define the routes

@return [Hanami::Routing::Route]

@since 0.2.0

@example In Hanami framework

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

# In `config/routes`

define do
  get # ...
end
# File lib/hanami/router.rb, line 276
def define(&blk)
  instance_eval(&blk) if block_given?
end
defined?() click to toggle source

Check if there are defined routes

@return [TrueClass,FalseClass] the result of the check

@since 0.2.0 @api private

@example

router = Hanami::Router.new
router.defined? # => false

router = Hanami::Router.new { get '/', to: ->(env) { } }
router.defined? # => true
# File lib/hanami/router.rb, line 294
def defined?
  @router.routes.any?
end
delete(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@see Hanami::Router#get

@since 0.1.0

# File lib/hanami/router.rb, line 488
def delete(path, options = {}, &blk)
  @router.delete(path, options, &blk)
end
get(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@since 0.1.0

@example Fixed matching string

require 'hanami/router'

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

@example 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] }!"]
    ]
  }

@example Variables Constraints

require 'hanami/router'

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

@example String matching with globbling

require 'hanami/router'

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

@example 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] }!"]]
  }

@example 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"

@example 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

@example 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

@example 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.
# File lib/hanami/router.rb, line 412
def get(path, options = {}, &blk)
  @router.get(path, options, &blk)
end
inspector() click to toggle source

Returns an routes inspector

@since 0.2.0

@see Hanami::Routing::RoutesInspector

@example

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
# File lib/hanami/router.rb, line 1216
def inspector
  require 'hanami/routing/routes_inspector'
  Routing::RoutesInspector.new(@router.routes, @router.prefix)
end
mount(app, options) click to toggle source

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.

@param app [#call] a class or an object that responds to call @param options [Hash] the options to customize the mount @option options [:at] the relative path where to mount the app

@since 0.1.1

@example 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

@example 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)

@example 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)
# File lib/hanami/router.rb, line 1007
def mount(app, options)
  @router.mount(app, options)
end
namespace(namespace, &blk) click to toggle source

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.

@param namespace [String] the relative path where the nested routes will

be mounted

@param blk [Proc] the block that defines the resources

@return [Hanami::Routing::Namespace] the generated namespace.

@since 0.1.0

@see Hanami::Router

@example Basic example

require 'hanami/router'

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

@example 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

@example

require 'hanami/router'

router = Hanami::Router.new
router.namespace 'trees' do
  get '/sequoia', to: endpoint # => '/trees/sequoia'
end
# File lib/hanami/router.rb, line 673
def namespace(namespace, &blk)
  Routing::Namespace.new(self, namespace, &blk)
end
options(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@see Hanami::Router#get

@since 0.1.0

# File lib/hanami/router.rb, line 594
def options(path, options = {}, &blk)
  @router.options(path, options, &blk)
end
patch(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@see Hanami::Router#get

@since 0.1.0

# File lib/hanami/router.rb, line 469
def patch(path, options = {}, &blk)
  @router.patch(path, options, &blk)
end
path(route, *args) click to toggle source

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.

@param route [Symbol] the route name

@return [String]

@raise [Hanami::Routing::InvalidRouteException] when the router fails to

recognize a route, because of the given arguments.

@since 0.1.0

@example

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"
# File lib/hanami/router.rb, line 1164
def path(route, *args)
  @router.path(route, *args)
end
post(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@see Hanami::Router#get

@since 0.1.0

# File lib/hanami/router.rb, line 431
def post(path, options = {}, &blk)
  @router.post(path, options, &blk)
end
put(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@see Hanami::Router#get

@since 0.1.0

# File lib/hanami/router.rb, line 450
def put(path, options = {}, &blk)
  @router.put(path, options, &blk)
end
recognize(env, options = {}, params = nil) click to toggle source

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.

@param env [Hash, String, Symbol] Rack env, path or route name @param options [Hash] a set of options for Rack env or route params @param params [Hash] a set of params

@return [Hanami::Routing::RecognizedRoute] the recognized route

@since 0.5.0

@see Hanami::Router#env_for @see Hanami::Routing::RecognizedRoute

@example 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"}

@example 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"}

@example 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"}

@example 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

@example 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

@example 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

@example 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

@example 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"}
# File lib/hanami/router.rb, line 1130
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) click to toggle source

Defines an HTTP redirect

@param path [String] the path that needs to be redirected @param options [Hash] the options to customize the redirect behavior @option options [Fixnum] the HTTP status to return (defaults to `301`)

@return [Hanami::Routing::Route] the generated route.

This may vary according to the `:route` option passed to the initializer

@since 0.1.0

@see Hanami::Router

@example

require 'hanami/router'

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

@example

require 'hanami/router'

router = Hanami::Router.new
router.redirect '/legacy',  to: '/new_endpoint'
# File lib/hanami/router.rb, line 624
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) click to toggle source

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

@param name [String] the name of the resource @param options [Hash] a set of options to customize the routes @option options [Array<Symbol>] :only a subset of the default routes

that we want to generate

@option options [Array<Symbol>] :except prevent the given routes to be

generated

@param blk [Proc] a block of code to generate additional routes

@return [Hanami::Routing::Resource]

@since 0.1.0

@see Hanami::Routing::Resource @see Hanami::Routing::Resource::Action @see Hanami::Routing::Resource::Options

@example 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      |
# +--------+----------------+-------------------+----------+----------------+

@example 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      |
# +--------+----------------+------------------+----------+----------------+

@example 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      |
# +--------+----------------+------------------+----------+----------------+

@example 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 |
# +--------+--------------------+--------------------+------+--------------------+

@example 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 |
# +------+----------------+----------------+------+----------------+
# File lib/hanami/router.rb, line 795
def resource(name, options = {}, &blk)
  Routing::Resource.new(self, name, options.merge(separator: @router.action_separator), &blk)
end
resources(name, options = {}, &blk) click to toggle source

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

@param name [String] the name of the resource @param options [Hash] a set of options to customize the routes @option options [Array<Symbol>] :only a subset of the default routes

that we want to generate

@option options [Array<Symbol>] :except prevent the given routes to be

generated

@param blk [Proc] a block of code to generate additional routes

@return [Hanami::Routing::Resources]

@since 0.1.0

@see Hanami::Routing::Resources @see Hanami::Routing::Resources::Action @see Hanami::Routing::Resource::Options

@example 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      |
# +--------+--------------------+-------------------+----------+----------------+

@example 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   |
# +------+-----------+-----------------+--------+-------------+

@example 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      |
# +--------+--------------------+-------------------+----------+----------------+

@example 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 |
# +--------+-----------------------+-------------------+------+-------------------+

@example 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 |
# +------+------------------+------------------+------+------------------+
# File lib/hanami/router.rb, line 918
def resources(name, options = {}, &blk)
  Routing::Resources.new(self, name, options.merge(separator: @router.action_separator), &blk)
end
root(options = {}, &blk) click to toggle source

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

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@since 0.7.0

@example Fixed matching string

require 'hanami/router'

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

@example 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/"
# File lib/hanami/router.rb, line 575
def root(options = {}, &blk)
  @router.get(ROOT_PATH, options.merge(as: :root), &blk)
end
routes() click to toggle source

Returns self

This is a duck-typing trick for compatibility with `Hanami::Application`. It's used by `Hanami::Routing::RoutesInspector` to inspect both apps and routers.

@return [self]

@since 0.2.0 @api private

# File lib/hanami/router.rb, line 252
def routes
  self
end
trace(path, options = {}, &blk) click to toggle source

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

@param path [String] the relative URL to be matched

@param options [Hash] the options to customize the route @option options [String,Proc,Class,Object#call] :to the endpoint

@param blk [Proc] the anonymous proc to be used as endpoint for the route

@return [Hanami::Routing::Route] this may vary according to the :route

option passed to the constructor

@see Hanami::Router#get

@since 0.1.0

# File lib/hanami/router.rb, line 507
def trace(path, options = {}, &blk)
  @router.trace(path, options, &blk)
end
url(route, *args) click to toggle source

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.

@param route [Symbol] the route name

@return [String]

@raise [Hanami::Routing::InvalidRouteException] when the router fails to

recognize a route, because of the given arguments.

@since 0.1.0

@example

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"
# File lib/hanami/router.rb, line 1191
def url(route, *args)
  @router.url(route, *args)
end

Protected Instance Methods

env_for(env, options = {}, params = nil) click to toggle source

Fabricate Rack env for the given Rack env, path or named route

@param env [Hash, String, Symbol] Rack env, path or route name @param options [Hash] a set of options for Rack env or route params @param params [Hash] a set of params

@return [Hash] Rack env

@since 0.5.0 @api private

@see Hanami::Router#recognize @see www.rubydoc.info/github/rack/rack/Rack%2FMockRequest.env_for

# File lib/hanami/router.rb, line 1236
def env_for(env, options = {}, params = nil)
  env = case env
  when String
    Rack::MockRequest.env_for(env, options)
  when Symbol
    begin
      url = path(env, params || options)
      return env_for(url, options)
    rescue Hanami::Routing::InvalidRouteException
      {}
    end
  else
    env
  end
end