Hanami, a better Rails?
March 22, 2020
For some time now, I have been observing from afar Hanami, the framework previously known as Lotus. Hanami is promoted as a “modern web framework for Ruby”. What I see in Hanami is a new version of Rails re-designed from the ground up. It is still a full featured framework that has everything needed to build web applications, actions, views, layouts, templates, mailers, models, ORM, etc. So this isn’t like Sinatra or Merb, a lightweight framework that only is useful for very simple web application, it’s a batteries-included full-fledged web framework.
Hanami differs from Rails in several ways. First is the idea that each component of the framework should stand on its own. This is something that has always bugged me about Rails. It is very hard and unintuitive to use most of the components of Rails in a standalone, outside of Rails way. Not that you really do need to do this a lot, but where I did find this to be annoying is when debugging issues where you need to go into the internal implementation of Rails. I always found it to be unnecessarily complicated. I would often ask myself “why is this this hard?” and “why is there this much code required to do X?”.
For example, why can’t you just instantiate a controller, call it and get the response? There are several components like this, and as a side project, I started implementing things like this on my own. For example, I created Rack::Action. The idea is that each action is its own class, instead of multiple actions per controller. If you want to share functionality, you use inheritance. Each action ends up being a Rack app. It’s small (few hundred lines of code), fast and easy to use and understand. So I was excited to see that Hanami has actions that work the same way.
Next is routing. Now that each action is its own Rack app, it’s easy to think of routing as just having a router this is also itself a Rack app and all it does is find the right Rack app, call it and then return that response. So this is what lead me to create Rack::Router. Again, it’s small (few hundred lines of code), fast and easy to use and understand. You can write a few lines of standard Ruby, instantiate a router and use it, without even having to generate a project. And again in Hanami, the router works very similar.
So I’ll go on to views and templates. This is another one that bothers me about Rails. Why can’t I just instantiate a class and call a method to render a template? Do you know how to do that in Rails? Could you go into the console and just call a method where you give it a Hash and the name of template and it would give you the rendered result? This is why I created Curtain. Again, same philosophy here, a simple to use standalone component, small, fast, etc. This one also has a design difference from Rails, where you have a class that is called a view, that is separate from the actual template. Rails calls templates “views”, which never really made sense to me. In the terminology I use in Curtain, a View is the object that is the context that the template is rendered within. Views eliminate the need for “helpers”, because you can just define methods on the view and then call them from the template. You can organize your helpers into modules and include them in specific views as needed. This is another one where Hanami follows the same philosophy as Curtain, including separate view classes from the template.
Another thing to call out is code reloading. To avoid having to restart your application in development, Rails has a lot of code to take care of that for you. A much simpler way of achieving the same effect is to use Shotgun. This is what Hanami recommends and what I have used in non-Rails Rack applications in the past as well. It is another prudent choice here by Hanami to just encourage the use of Shotgun rather than complicate the framework with this functionality.
The last thing that I’ll point out that is something where I’ve independently come to the same conclusion as Hanami is what Hanami calls Interactors. I have to admit that I wasn’t familiar with the term Interactor until I saw it in the Hanami docs, but I’m definitely familiar with the concept. The basic idea here is that for somewhat complex business or persistent logic, that involve maybe wrapping multiple persistence calls in one transaction, instead of having that pollute your models with class or instance methods, write an Interactor for each of these operations. In my applications, I’ve been calling these things “Services” and I’ve heard that same terminology used by others. I have found this pattern to be a huge improvement in code testability, reusability and organization. This is such an improvement in the quality of application architecture that it is a bit of head scratcher to me that there isn’t something in Rails like this, if for no other reason to standardize and encourage the pattern.
I haven’t had a chance to build a real application with Hanami yet, but I’d like to give it a try at some point. There are so many design decisions in Hanami I agree with and have considered myself in the past. 7 years ago, I started to work on a framework that I was calling Sharp that pulled together all the components that I have mentioned in this article in a similar way to what Hanami has. I never got the opportunity to get it to be production ready, “life got in the way” as they say, but it is nice to see something complete and polished that is similar philosophically in so many ways.