hanami_ruby

Hanami – Ruby Web Framework [Review]

   Back to list

If you think that Rails and Sinatra are the two most popular Ruby web frameworks, you’re probably right. Thanks to a big community and numerous gems, Rails is the biggest and most popular Ruby framework for now. Sinatra is also quite popular… but generally for smaller web apps. If you have ever tried to build something with it – it’s quite hard to build something big.

There are also two less popular frameworks – Pardino and Lotus. Well, Lotus is no longer Lotus – now it’s called Hanami. I thought, that since Lotus changed its name, maybe they also added a lot of new features and improvements and maybe we can begin to count it as a Rails rival.

In this article, I want to compare Hanami to Rails and write a little about its features. I also wanted to share my feelings after spending some time building a test application.

It’s about Hanami v1.0.0 (released 6th April 2017).

What is Hanami?

First of all – what is Hanami? Hanami is a full-stack Ruby web framework built by Luca Guidi, made up of simple, small Ruby libraries. As the Hanami Team writes on their page, Hanami’s goal is to build lightweight apps which consume less memory than other Ruby web frameworks (they probably had in mind Rails).

Let’s look up at Hanami’s core. It’s built of 9 main parts:

  • Hanami::Model – Persistence with entities, repositories and data mapper
  • Hanami::View – Presentation with a separation between views and templates
  • Hanami::Controller – Full featured, fast and testable actions for Rack
  • Hanami::Validations – Validations mixin for Ruby objects
  • Hanami::Router – Rack compatible HTTP router for Ruby
  • Hanami::Helpers – View helpers for Ruby applications
  • Hanami::Mailer – Mail for Ruby applications
  • Hanami::Assets – Assets management for Ruby
  • Hanami::Utils – Ruby core extensions and class utilities

As you can see, these parts look similar to the Rails core:

  • Hanami::Model -> ActiveModel
  • Hanami::View -> ActionView
  • Hanami::Controller -> ActionController
  • Hanami::Validations -> ActiveModel::Validations
  • Hanami::Router -> ActionDispatch::Routing
  • Hanami::Helpers -> ActionController::Helpers
  • Hanami::Mailer -> ActionMailer
  • Hanami::Assets -> The Asset Pipeline
  • Hanami::Utils -> ActiveSupport

Architecture

First of all, there is a physical separation in a file’s structure between what happens on the front-end and the back-end. Controllers, which are responsible for processing requests, manage response logic and provide data to views. They are included in apps/web.

Views are also stored there, but we can divide them into templates and views. Templates are normal .html.erb views, while views are something like helpers. You can write a helper which builds a full HTML form and include it in a template. Moreover, under the web/config folder, you can find a router which includes all routes.

Also, as in Rails, all assets like images, javascript files, and CSS files are located under web/assets folder.

Everything that is connected to the back-end logic, like models and mailers, are stored in the lib/app_name folder. It’s pretty logical.

What’s more interesting is that models are divided into two classes – entities and repositories. Entities are plain, Ruby objects which define all instance methods, while repositories are classes responsible for defining database associations, scopes, query construction and defining a connection with the database.

What’s more, Hanami allows you to create many applications under one application. What does it mean? For instance, you have an application for admins and users, different dashboards and actions – you can create them under one app. Admins’ app will be located under the lib/admins, while users’ app under the lib/users. In Rails, we can achieve it by using namespaces.

Generators

Hanami has built in generators like Rails. A lot of things also look similar, for instance:

$ hanami generate model model_name
$ hanami generate migration migration_name

Also, we can run a console, server or database console using the hanami command:

$ hanami console
$ hanami db console
$ hanami server

But if you want to migrate a database, you don’t need to run Rake, instead use:

$ hanami db migrate

Code-reloading

In Rails, we have a great feature which reloads our code in a background without needing to restart a server. Hanami also has the same feature. It uses a gem called Shotgun to auto-reload any code modifications in the background.

Models

What is different and maybe even better in Hanami? First of all, in Rails, a database table is represented as a model. By default, everything is there and should be defined there. As I mentioned before, in Hanami, models are divided into Entities and Repositories. Moreover, they are located in the lib folder, not an app like in Rails.

What is the difference and for what are they used? Well, Entities are pure Ruby object which collects data returned by a repository from a database. You can’t establish a connection to a table while calling a model like MyModel.create. If you want to do so, you need to use a repository.

Repositories are objects which get data from a database and as a result, return a collection of models. For example, if you want to get data from the database, you should run MyModelRepository.new.all. Every time when you want to access the database, you should create a new instance of a repository object.

Moreover, in repositories, we keep all logic connected with a database – queries, scopes and associations, like:

class FlightRepository < Hanami::Repository
   associations do
     has_many :passengers
   end
  
  def count
    flights.count
  end

  def most_recent_flights(limit = 10)
    flights
      .order(Sequel.desc(:flights__departure_at))
      .limit(limit)
  end
end

What is funny? The fact that, by default, something like MyModelRepository.new.count doesn’t work – you need to define the count method in order to get COUNT(*), like in the code above. In models, everything stays connected with objects – like instance methods.

Honestly, IMHO this division is a great thing, the two layers of logic connected with models are separated and the code is cleaner. We know where we should put a new logic.

What is bad? In the following beta version, the Hanami Team added new relations like belongs_to, has_one and has_many :through. For me it’s like a base for models, without this it’s really hard to define any relation in a database – and yes, the live version doesn’t have it yet.

Controllers

Controllers look similar to the Rails’ – they are stored in the web/controllers folder. What is different? Well, in Hanami there isn’t a single controller for an each resource, like BooksController. Also, all actions connected to a resource aren’t in one file. Each action of the controller has its own file. This file and class name equal to the an action name. There is only one method called call – which handles all logic.

There are a couple of important things. First, each file should be in a controller namespace, like: Web::Controllers::Home. Second, instance variables are not shared between views, to access it in a view, you’ll need to expose it, using the expose method, like:

module Web::Controllers::Home
  class Index
    include Web::Action
    expose :airports

    def call(params)
      @airports = AirportRepository.new.all
    end
  end
end

Views

As I said before, views are divided into two types – templates and views. Templates are ordinary .html.erb files, where you can keep the complete HTML, while views are like Rails helpers. You can define their Ruby methods which can be accessible in a template. Like controllers, each action has its own template and view.

This is how a template looks:

<div class="row">
  <div class="col-md-8">
    <div class="jumbotron">
      <h1>Search for flights:</h1>
      <%= search_form %>
    </div>
  </div>
</div>

This is how a view looks:

module Web::Views::Home
  class Index
    include Web::View

    def search_form
      form_for :search, '/search', method: :get do
        div class: 'form-group' do
          label :departure_airport_id
          select :departure_airport_id, airports.map { |a| [a.name_with_code, a.id] }, class: 'form-control', options: { prompt: 'Select a departure airport...' }
        end

        div class: 'form-group' do
          label :arrival_aiport_id
          select :arrival_aiport_id, airports.map { |a| [a.name_with_code, a.id] }, class: 'form-control', options: { prompt: 'Select an arrival airport...' }
        end

        div class: 'form-group' do
          label :departure_date
          date_field :departure_date, class: 'form-control'
        end

        submit 'Search', class: 'btn btn-success'
      end
    end
  end
end

Well, this structure looks very organized to me. Each action has its own separate file, so everything looks clear and files aren’t too big. That’s the plus for Hanami! But on the other hand, this division isn’t something new, we have it already in Rails divided into views and helpers.

Migrations

Like Rails, Hanami also provides database migrations which help manage any changes in a database table. They look pretty similar to Rail’s migrations but IMHO they are more clear – their syntax.

Hanami::Model.migration do
  change do
    create_table :airports do
      primary_key :id

      column :code,       String,   null: false, unique: true, size: 3
      column :name,       String,   null: false, unique: true
      column :city,       String,   null: false
      column :country,    String,   null: false
      column :created_at, DateTime, null: false
      column :updated_at, DateTime, null: false

      index :code, unique: true
      index :name, unique: true
    end
  end
end

My thoughts and feelings

I tried to make a sample application in Hanami to check how this framework really looks. You know, in the documentation everything can be beautiful and easy. I gave myself a 6-hour limit to create something that really works.

I wanted to create an application which searches for flights by specified departure and arrival airports and also by a date. A flight can be booked by a passenger. Everything is saved in a database. Well, I’m not saying that it’s easy to achieve in 6 hours but I think that a similar MVP in Rails is creatable in 6. You just create all models and a search method which looks for a specified flight in a database (you have dummy data) and later you can book it. Yeah, it’s not so complicated and we try to keep it as simple as it could be. Of course, we don’t add any custom styles and tests, just plain Bootstrap. And before we start, a database schema is ready, so we know all associations.

So here is my story about how it looked like in Hanami. First of all, I started from creating models. That was really easy – I created all needed migrations, entities and repositories. Migrated everything together and created a service, which seeds my database with dummy data – in Hanami there isn’t something like rake db:seeds in Rails.

When I had everything ready, I started creating the main controller, which is responsible for getting all available airports from the database and creating a search form. That wasn’t so hard but here I learned that instance variables are shared with views. You need to explicitly define which variable is shared with a view.

I also learned here, that you can’t think like in Rails, you need to get data from a model’s repository, not directly from a model – moreover, we don’t have them in Hanami. I also added the first method in an entity, which was responsible for formatting data and was shown on the front-end. Wohooo, I used all models mechanisms that Hanami provides!

After this part, I started defining associations in the repositories and defining the scopes. The first weird thing was the fact, that Hanami doesn’t provide built in count method, you need to define it by yourself. Ok, I thought that it’s not something hard and bad at all, you can define it in 10 seconds. But the next weird thing was the fact, that Hanami doesn’t provide the belongs_to association. I was digging in the source code and other libraries, like Sequel to find out why that doesn’t work. Yeah, Hanami doesn’t have belongs_to or has_one associations (one to many!).

Another thing was the fact, that without it, it’s hard for me to query needed fields – you need to write a pure SQL query to get it. Yeah, you use a library to use pure SQL – pretty normal, right? What’s more, for me their documentation at some level really sucks. There are like one or two examples and that’s everything. You need to guess how to use a function or dig into the source code to check all available options. Imagine how much time you will waste when you need to research every function like this, its behavior etc.

What’s even funnier, is the fact that in a simple SQL query I needed to use a Sequel method, to get what I needed.

When I was somehow done with this part, I moved on to the views. I had some problems defining a route in a form, and yes, I followed the documentation. All the time I was getting the same error, the route is not found. I was googling and digging in the source code without any success. Then I realized that I spent like 2-3 hours to get what I needed. Yeah, at this point I saw that simple things in Hanami took too much time.

Conclusion

For me, Hanami is a great project and idea! Its architecture is planned really well and it’s really clear. But there is a big problem – for now, it is a lack of really good documentation with more complex examples. What’s more, the community isn’t big at the moment, so if you think that you’ll find an answer for your problem on StackOverflow or in Google or in the documentation, you’re probably wrong.

The biggest problem is the fact that the most Ruby gems aren’t created or designed with Hanami use in mind. So if you want to create a fast MVP and you don’t have a big budget or long timeline, you probably shouldn’t create an app with Hanami. As for me, it’s not ready for big production apps yet. You can build simple apps like a blog or something similar.

I hope that you like this article. Remember one big, important thing! I didn’t spend a lot of time developing web-apps with Hanami so I might be wrong, but at the first sight, I would still choose Rails and for me, it’s not well-documented yet.

Looking forward to hearing from you!

Send this to a friend