• Stars
    star
    360
  • Rank 114,424 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 11 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Ruby/Rack HTTP router

Hanami::Router

Rack compatible, lightweight and fast HTTP Router for Ruby and Hanami.

Version

This branch contains the code for hanami-router 2.x.

Status

Gem Version CI Test Coverage Depfu Inline Docs

Contact

Rubies

Hanami::Router supports Ruby (MRI) 3.0+

Installation

Add this line to your application's Gemfile:

gem "hanami-router"

And then execute:

$ bundle

Or install it yourself as:

$ gem install hanami-router

Getting Started

Create a file named config.ru

# frozen_string_literal: true
require "hanami/router"

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

run app

From the shell:

$ bundle exec rackup

Usage

Hanami::Router is designed to work as a standalone framework or within a context of a Hanami application.

For the standalone usage, it supports neat features:

A Beautiful DSL:

Hanami::Router.new do
  root                to: ->(env) { [200, {}, ["Hello"]] }
  get "/lambda",      to: ->(env) { [200, {}, ["World"]] }
  get "/dashboard",   to: Dashboard::Index
  get "/rack-app",    to: RackApp.new

  redirect "/legacy", to: "/"

  mount Api::App, at: "/api"

  scope "admin" do
    get "/users", to: Users::Index
  end
end

Fixed string matching:

Hanami::Router.new do
  get "/hanami", to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
end

String matching with variables:

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

Variables Constraints:

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

String matching with globbing:

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

String matching with optional tokens:

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

Support for the most common HTTP methods:

endpoint = ->(env) { [200, {}, ["Hello from Hanami!"]] }

Hanami::Router.new do
  get     "/hanami", to: endpoint
  post    "/hanami", to: endpoint
  put     "/hanami", to: endpoint
  patch   "/hanami", to: endpoint
  delete  "/hanami", to: endpoint
  trace   "/hanami", to: endpoint
  options "/hanami", to: endpoint
end

Root:

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

Redirect:

Hanami::Router.new do
  get "/redirect_destination", to: ->(env) { [200, {}, ["Redirect destination!"]] }
  redirect "/legacy",          to: "/redirect_destination"
end

Named routes:

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

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

Scopes:

router = Hanami::Router.new do
  scope "animals" do
    scope "mammals" do
      get "/cats", to: ->(env) { [200, {}, ["Meow!"]] }, as: :cats
    end
  end
end

# and it generates:

router.path(:animals_mammals_cats) # => "/animals/mammals/cats"

Mount Rack applications:

Mounting a Rack application will forward all kind of HTTP requests to the app, when the request path matches the at: path.

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

Duck typed endpoints:

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

Hanami::Router.new do
  get "/hanami",     to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
  get "/rack-app",   to: RackApp.new
  get "/method",     to: ActionControllerSubclass.action(:new)
end

Implicit Not Found (404):

router = Hanami::Router.new
router.call(Rack::MockRequest.env_for("/unknown")).status # => 404

Explicit Not Found:

router = Hanami::Router.new(not_found: ->(_) { [499, {}, []]})
router.call(Rack::MockRequest.env_for("/unknown")).status # => 499

Body Parsers

Rack ignores request bodies unless they come from a form submission. If we have a JSON endpoint, the payload isn't available in the params hash:

Rack::Request.new(env).params # => {}

This feature enables body parsing for specific MIME Types. It comes with a built-in JSON parser and allows to pass custom parsers.

JSON Parsing

# frozen_string_literal: true

require "hanami/router"
require "hanami/middleware/body_parser"

app = Hanami::Router.new do
  patch "/books/:id", to: ->(env) { [200, {}, [env["router.params"].inspect]] }
end

use Hanami::Middleware::BodyParser, :json
run app
curl http://localhost:2300/books/1    \
  -H "Content-Type: application/json" \
  -H "Accept: application/json"       \
  -d '{"published":"true"}'           \
  -X PATCH

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

If the json can't be parsed an exception is raised:

Hanami::Middleware::BodyParser::BodyParsingError
multi_json

If you want to use a different JSON backend, include multi_json in your Gemfile.

Custom Parsers

# frozen_string_literal: true

require "hanami/router"
require "hanami/middleware/body_parser"

# See Hanami::Middleware::BodyParser::Parser
class XmlParser < Hanami::Middleware::BodyParser::Parser
  def mime_types
    ["application/xml", "text/xml"]
  end

  # Parse body and return a Hash
  def parse(body)
    # parse xml
  rescue SomeXmlParsingError => e
    raise Hanami::Middleware::BodyParser::BodyParsingError.new(e)
  end
end

app = Hanami::Router.new do
  patch "/authors/:id", to: ->(env) { [200, {}, [env["router.params"].inspect]] }
end

use Hanami::Middleware::BodyParser, XmlParser
run app
curl http://localhost:2300/authors/1 \
  -H "Content-Type: application/xml" \
  -H "Accept: application/xml"       \
  -d '<name>LG</name>'               \
  -X PATCH

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

Testing

# frozen_string_literal: true

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"
route.endpoint  # => "books.show"
route.params    # => {:id=>"23"}
route.routable? # => true

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

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

Versioning

Hanami::Router uses Semantic Versioning 2.0.0

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Copyright

Copyright © 2014-2022 Hanami Team – Released under MIT License

More Repositories

1

hanami

The web, with simplicity.
Ruby
6,147
star
2

model

Ruby persistence framework with entities and repositories
Ruby
445
star
3

api

Minimal, lightweight, fastest Ruby framework for HTTP APIs.
Ruby
342
star
4

controller

Complete, fast and testable actions for Rack and Hanami
Ruby
245
star
5

validations

Validation mixin for Ruby objects
Ruby
213
star
6

utils

Ruby core extentions and class utilities for Hanami
Ruby
173
star
7

view

Views, templates and presenters for Ruby web applications
Ruby
171
star
8

hanami-2-application-template

Hanami 2 application starter template
Ruby
107
star
9

hanami.github.io

Hanami website
HTML
88
star
10

helpers

View helpers for Ruby applications
Ruby
79
star
11

guides

Hanami Guides
CSS
51
star
12

mailer

Mail for Ruby applications
Ruby
48
star
13

assets

Assets management for Ruby web applications
Ruby
47
star
14

events

[Experimental] Events framework for Hanami
Ruby
43
star
15

cli

Hanami command line
Ruby
25
star
16

bookshelf

Hanami "Getting Started" project
Ruby
21
star
17

contributors

All hanami contributors in one place
Ruby
14
star
18

reloader

Code reloading for Hanami 2
Ruby
14
star
19

ecosystem

Hanami Ecosystem
12
star
20

snippets

Learn Hanami by reading, short, curated, manually crafted code snippets.
CSS
10
star
21

webconsole

Hanami web console for development
Ruby
7
star
22

community

7
star
23

ujs

Hanami UJS
Ruby
4
star
24

devtools

Hanami development tools [internal usage]
Ruby
4
star
25

docs

Hanami API Docs
CSS
4
star
26

rspec

Hanami RSpec extensions
Ruby
3
star
27

assets-js

esbuild plugin for Hanami Assets
TypeScript
3
star
28

guides-v2

Working area for new Hanami 2.0 guides; these will be merge into the main guides repo before 2.0 release.
2
star
29

model-sql

Hanami Model SQL
Ruby
1
star
30

playbook

Ruby
1
star
31

docker-ci-images

Docker images to be used with our CI
Dockerfile
1
star