Requests Handling

In the previous section, we generated an action. Now let’s use it.

First, we check our routes:

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

View Rendering

Then we edit the corresponding template:

  1. # apps/web/templates/dashboard/index.html.erb
  2. <h1>Dashboard</h1>

Here is how Hanami handles an incoming request:

  1. The router creates a new instance of Web::Controllers::Dashboard::Index and invokes #call.
  2. The application creates a new instance of Web::Views::Dashboard::Index and invokes #render.
  3. The application returns the response to the browser.

For a given action named Web::Controllers::Dashboard::Index, a corresponding view MUST be present: Web::Views::Dashboard::Index.

If we visit /dashboard we should see <h1>Dashboard</h1> in our browser.

Bypass Rendering

By default an action takes care of the HTTP status code and response header, but not of the body of the response. As seen above, it delegates the corresponding view to render and set this value.

Sometimes we want to bypass this process. For instance we want to return a simple body like OK. To involve a view in this case is a waste of CPU cycles.

If we set the body of the response from an action, our application will ignore the view.

  1. # apps/web/controllers/dashboard/index.rb
  2. module Web
  3. module Controllers
  4. module Dashboard
  5. class Index
  6. include Web::Action
  7. def call(params)
  8. self.body = 'OK'
  9. end
  10. end
  11. end
  12. end
  13. end

Here is how Hanami handles an incoming request in this case:

  1. The router creates a new instance of Web::Controllers::Dashboard::Index and invokes #call.
  2. The application detects that a body is already set and doesn’t instantiate the view.
  3. The application returns the response to the browser.

If we visit /dashboard again, now we should see OK.

If the response body was already set by an action, the rendering process is bypassed.

With direct body assignment, we can safely delete the corresponding view and template.

Initialization

Actions are instantiated for us by Hanami at the runtime: for each incoming request, we’ll automatically get a new instance. Because actions are objects, we can take control on their initialization and eventually inject our dependencies. This is a really useful technique for unit testing our actions.

  1. # apps/web/controllers/dashboard/index.rb
  2. module Web
  3. module Controllers
  4. module Dashboard
  5. class Index
  6. include Web::Action
  7. def initialize(greeting: Greeting.new)
  8. @greeting = greeting
  9. end
  10. def call(params)
  11. self.body = @greeting.message
  12. end
  13. end
  14. end
  15. end
  16. end

There is a limitation that should always be kept in mind:

Action initializer MUST have an arity of 0.

The following initializers are valid:

  1. # no arguments
  2. def initialize
  3. # ...
  4. end
  5. # default arguments
  6. def initialize(greeting = Greeting.new)
  7. # ...
  8. end
  9. # keyword arguments
  10. def initialize(greeting: Greeting.new)
  11. # ...
  12. end
  13. # options
  14. def initialize(options = {})
  15. # ...
  16. end
  17. # options
  18. def initialize(**options)
  19. # ...
  20. end
  21. # splat arguments
  22. def initialize(*args)
  23. # ...
  24. end