String matching with optional tokens:

Support for the most common HTTP methods:

router=Hanami::Router.newendpoint=->(env){[200,{},['Hello from Hanami!']]}router.get'/hanami',to:endpointrouter.post'/hanami',to:endpointrouter.put'/hanami',to:endpointrouter.patch'/hanami',to:endpointrouter.delete'/hanami',to:endpointrouter.trace'/hanami',to:endpoint

Root:

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

Namespaced routes:

router=Hanami::Router.newrouter.namespace'animals'donamespace'mammals'doget'/cats',to:->(env){[200,{},['Meow!']]},as::catsendend# and it generates:router.path(:animals_mammals_cats)# => "/animals/mammals/cats"

Duck typed endpoints:

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

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

If it's a string, it tries to instantiate a class from it:

classRackAppdefcall(env)# ...endendrouter=Hanami::Router.newrouter.get'/hanami',to:'rack_app'# it will map to RackApp.new

It also supports Controller + Action syntax:

moduleFlowersclassIndexdefcall(env)# ...endendendrouter=Hanami::Router.newrouter.get'/flowers',to:'flowers#index'# it will map to Flowers::Index.new