V1.3: Overview


Principles

A Hanami view is an object that defines presentational logic. Helpers are modules designed to enrich views with a collection of useful features.

This concept is probably familiar, if you know some Ruby basics.

module Printable
  def print
    puts "..."
  end
end

class Person
  include Printable
end

Person.new.print

The same simple design is applied to views and helpers.

Hanami ships with default helpers, but we can also define custom helper modules.

Rendering Context

Views are Ruby objects that are responsible for rendering the associated template. The context for this activity is defined only by the set of methods that a view can respond to.

If a view has a method #greeting, we can use it like this: <%= greeting %>.

This design has a few important advantages:

  • It facilitates debugging. In the case of an exception, we know that the view is the only rendering context to inspect.
  • Ruby method dispatcher will be fast, as it doesn’t need to do a lookup for many method sources.

Consider the following code:

# apps/web/views/books/show.rb
module Web::Views::Books
  include Web::View

  def home_page_link
    link_to "Home", "/"
  end
end

Our view responds to #link_to, because it includes Hanami::Helpers::LinkToHelper, a module that defines that concrete method.

Clean Context

There are some helpers that have a huge interface. Think of the HTML5 or the routing helpers, they provide hundreds of methods to map tags or application routes.

Making them available directly in the view context, would be source of confusion, slow method dispatch times and name collisions.

Imagine we have an application with 100 routes. Because Hanami provides both relative and absolute URI facilities, if used directly, it would mean adding 200 methods to all the views. Which is overkill.

For this reason, certain helpers act as a proxy to access these large set of methods.

<%= routes.home_path %>

In this case we have only one method to add to our views, but it opens an infinite number of possibilities without causing performance issues.

Explicit Interfaces

Hanami guides developers to design explicit and intention revealing interfaces for their objects. Almost all the default helpers, make private methods available to our views.

We want to avoid complex expressions that will clutter our templates, and make sure that views remain testable.

Here an example of poor and untestable code in a template.

<%= format_number book.downloads_count %>

If we want to unit test this logic, we can’t do it directly, unless we render the template and match the output.

For this reason #format_number, is shipped as a private method, so we are forced to define an explicit method for our interface.

# apps/web/views/books/show.rb
module Web::Views::Books
  include Web::View

  def downloads_count
    format_number book.downloads_count
  end
end

To be used like this:

<%= downloads_count %>

This version is visually simpler and testable.

Disable Helpers

Helpers aren’t mandatory for Hanami applications. If we want to get rid of them, we just to need to remove two lines of code.

# apps/web/application.rb
require 'hanami/helpers' # REMOVE THIS LINE

module Web
  class Application < Hanami::Application
    configure do
      # ...

      view.prepare do
        include Hanami::Helpers # AND THIS ONE
      end
    end
  end
end