Path matching

In our initial example we have introduced a really basic relative URI: /hello. This is what we call fixed path matching. It is called this because the segment is responsible for responding only to an exact match. If we visit /hello, we get a response. If we hit /foo, a 404 (Not Found) is returned.

Fixed Matching

  1. # apps/web/config/routes.rb
  2. get '/dashboard', to: "dashboard#index"

Variables

When we have dynamic content to serve, we want our URI to be dynamic as well. This can be easily achieved via path variables. They are defined with a colon, followed by a name (eg. :id).

Once an incoming request is forwarded to our endpoint, we can access the current value in our param’s action (params[:id]).

  1. get '/books/:id', to: 'books#show'

Multiple variables can be used in a path.

  1. get '/books/:book_id/reviews/:id', to: 'reviews#show'

Variables Constraints

It’s possible to specify constraints for each variable. The rule MUST be expressed as a regular expression. If a request can satisfy all of them, we’re good, otherwise a 404 is returned.

  1. get '/authors/:id', id: /\d+/, to: 'authors#show'

Optional Tokens

Sometimes we want to specify an optional token as part of our URI. It should be expressed between round parentheses. If present, it will be available as param in the Rack env, otherwise it will be missing, but the endpoint will be still hit.

  1. get '/books(.:format)', to: 'books#show'

Wildcard Matching

Imagine we want to serve static files from a user repository. It would be impossible to know in advance which files are stored and to prepare routes accordingly.

To solve this problem, Hanami supports wildcard matching.

  1. get '/files/*', to: 'files#show'

Named Routes

We can specify a unique name for each route, in order to generate paths from the router or to test them.

  1. root to: 'home#index'
  2. get '/hello', to: 'greet#index', as: :greeting
  3. get '/books/:id', to: 'books#show', as: :book

When a Hanami application starts, it generates a Ruby module at the runtime under our application namespace: eg. Web.routes. We can use it to generate a relative or absolute URI for our route.

  1. Web.routes.path(:root) # => "/"
  2. Web.routes.url(:root) # => "http://localhost:2300/"
  3. Web.routes.path(:greeting) # => "/hello"
  4. Web.routes.url(:greeting) # => "http://localhost:2300/hello"

When we have one or more variables, they can be specified as a Hash.

  1. Web.routes.path(:book, id: 1) # => "/books/1"
  2. Web.routes.url(:book, id: 1) # => "http://localhost:2300/books/1"

Absolute URL generation is dependent on scheme, host and port settings in apps/web/application.rb.

Routing Helpers

Generating routes from Web.routes is helpful, because that module can be accessed from anywhere. However, this syntax is noisy.

Hanami has routing helpers available as routes in: actions, views and templates.

  1. <%= routes.path(:greeting) %>
  2. <%= routes.url(:greeting) %>

Or

  1. <%= routes.greeting_path %>
  2. <%= routes.greeting_url %>

Namespaces

If we want to group a set of resources under a common prefix we can use namespace.

  1. namespace 'docs' do
  2. get '/installation', to: 'docs#installation'
  3. get '/usage', to: 'docs#usage'
  4. end
  5. # This will generate:
  6. #
  7. # /docs/installation
  8. # /docs/usage

Redirects

In case of legacy routes, we can handle HTTP redirects at the routing level.

  1. redirect '/old', to: '/new'