Hanami 2.0 Ideas


NPM/Webpack can be a simple solution:

cd app/web
brew install yarn # or OS equivalent
npm install -g hanami-webpack
yarn add hanami-ajax-forms
yarn install

An hanami-webpack npm package could be the base to install a webpack environment + a command to preprocess assets (with watch option).

This way, you have a simple way for ppl that don’t want to worry about it, but anyone needing a bit more can customize its pipeline. It can also easily be added in the app generation, and be entirely skipped if the app is an api.


This is basically the same idea as laravel-mix, just a stand alone npm package with a default webpack config.
@jodosha sorry for bringing up laravel stuff again :sweat:


New date helpers

Rails has helpful time and date helpers (like distance_of_time_in_words, distance_of_time_in_words_to_now and Date class).

I think we need to create helpers for working with the date. I start working on it but I’ll be happy to see other ideas and (or) feedback.


Container for application

We can create the global container for all hanami application. It will be super useful for initializers, some service objects, etc. In my mind I’m thinking about something like this:

# config/initializers/redis.rb

AppName::Container.register :redis do
  ConnectionPool.new(size: 10, timeout: 3) { Redis.new(host: 'localhost', port: 6379) }

# spec/spec_helper.rb

AppName::Container.register :redis do
  ConnectionPool.new(size: 10, timeout: 3) { MockRedis.new }

# in action, view, repository, etc

module Web::Controllers::Contributors
  class Show
    include Web::Action

    def call(params)
      user = container[:redis].with { |c| c.get(:key) }

In this example container[:redis] is not a part of hanami. Developer should define this container for his application by self.

Why containers will good for developers:

  1. you can set special container for each envs
  2. you will have general way for working with shared code
  3. hanami-gems developers will have the general way to share gem code in hanami app

Ideas where you can use it now

  1. container['interactors.interactor_name']
  2. Any code in /config/initialization/ folder (for example I haveMarkdown` class for converting md to html)
  3. some api clients without global configuration (twitter = Twitter.new(config); twitter.tweet('text'))
  4. any services from lib/ folder


@davydovanton I agree and I already outlined this proposal (see Hanami 2.0 Ideas, “IoC” section).

We already have a working implementation: Hanami::Components, which works very well. It’s a matter of enhance it a bit and mark as Public API. The plan is to rename it too.


GraphQL plugin for hanami

I’m not sure about this idea but I think we can create hanami-graphql plugin for graphql endpoint in hanami. This is a just idea and I’ll be happy to discuss it.

Some links:


Register options for Hanami::Components

Inspired by dry-containers


As followup on the CLI state: we implemented hanami-cli gem from scratch. It’s a replacement for thor, and it can be used to build CLIs with Ruby.

Based on that gem, we rewrote from scratch hanami CLI (aka all the hanami server commands et all). https://github.com/hanami/hanami/pull/808

It cuts down the maintenance burden for us, and it allows third-party developers to add their own generators and CLI commands.

This feature will be available as preview from August 2017 with hanami-1.1.0.beta1.


I see GraphQL as third-party gem, not in the core of the framework.


yep, only third-party gem without changing core lib :+1:


After discussion with @jodosha, we decided that this library should look like another gem.
Also, we think that hanami-events should use different adapters and be without a global state.

Why different adapters?

I think hanami events should be a library for building Event-Driven Architecture. In this case, a library should work with different hanami project instances:

In this case, we can provide different transport layers for a event storage like:

  • Memory
  • Postgres
  • Kafka
  • Kinesis
  • etc

And that’s why we need to replace wispers gem as an adapter.

From which hanami events should be?

I think that we need to split all logic to 3 different parts:

  1. Broadcaster
  2. Subscriber
  3. Routing

All these parts should be without a global state. And here my ideas about API for all this.


events = Hanami::Events.new(:adapter)
events.broadcast('user.created', user: user)


kafka_events = Hanami::Events.new(:kafka)
memory_events = Hanami::Events.new(:memory)

memory_events.subscribe('user.signup', Mailer)

# or in class
class Signup
  inclide Hanami::Events::Subscriber
  subscribe_to memory_events, 'user.signup'
  subscribe_to kafka_events, 'user.signup'

  def call(payload)
    # ...

Also, I think we should have an ability for subscribing to all events:

kafka_events = Hanami::Events.new(:kafka)

kafka_events.listen do |event|
  event.name    # => 'user.signup'
  event.payload # => { ... }


Routing should route all events and call specific code for each one. It will be helpful for manage all events in one place (look like HTTP routers):

class WebHandler < Hanami::Events::Handler
  on('user.created') { |payload| UserRepository.new.create(payload) }
  on('user.updated') { |payload| payload }

class AnaliticHandler < Hanami::Events::Handler
  on('*', AnaliticSender)
  on('user.*') { |payload| payload }
  on('user.signup') { |payload| payload }

class NotificationHandler < Hanami::Events::Handler
  on('user.created') { |payload| payload }
  on('post.created') { |payload| payload }

class EventRouting < Hanami::Events::Routing
  mount UserHandler, Hanami.app?(:user_events) # load specific handlers for instance
  mount AnaliticHandler
  mount NotificationHandler

events = Hanami::Events.new(:adapter)
events.listen { |event| EventRouter.new.resolve(event) }


We will start work on proof of concept for this idea. It’s mean that we will implement only broadcaster and simple subscriber without routing and other features.



Remove view escaping dark magic

class Web::Views::Foo::Index
  include Web::View

  def date

def test_view_method
  view = Web::Views::Foo::Index.new('apps/web/templates/foo/index.html.slim', Hash[])
  assert_equal "10/10/10", view.date
--- expected
+++ actual
@@ -1,2 +1,2 @@
-# encoding: UTF-8
+# encoding: ASCII-8BIT

This is because of this :mage:

Template engines

  • slim: escapes by default (== to unescape)
  • haml with :escape_html option: escapes by default (!= to unescape)
  • erb: doesn’t escape


Stop using ERB to parse .erb templates, instead, use Erubi, which has an :escape option, turning <%= to escaped and <%== to unescaped, thus rendering the :mage: useless for all 3 main template file formats.


Get ride of controller callbacks and use Laravel like aproach.

Middleware for route groups https://laravel.com/docs/5.6/authentication#protecting-routes, https://laravel.com/docs/5.6/authorization#via-middleware
Model binding https://laravel.com/docs/5.6/routing#route-model-binding


Switching to Roda sounds nice!


freeze object after expose :wink: and make view object immutable.


I’d like to have the ability to override public methods in a repository. See my comment here in an old issue.

I think it’d also be worth adding better support for UUID primary keys. A recent change to the guides is a great start, but I realized after I proposed that change that default orderings will no longer work. Take FooRepository#first for example: it will sort randomly generated UUID's and return the first. That doesn’t make sense with a randomly generated primary key. Maybe this could be handled in the generator, where it could add implementations of #first and #lastthat order(:created_at)?

Lastly, I’d love to see more attention brought to vendored assets. This was a great start but lacks support for vendored assets used across multiple apps. I had to create my own S3/CloudFront-based CDN to vendor these assets.


Are these being implemented anywhere? I’m not seeing any branches on Github with anything here really being implemented as of yet…


A couple of weeks ago we released v1.3, which marks the end of the 1.x series.

We’re jumping now on 2.x series. If you’re interested, please check unstable branches of all the repositories.


I’m gonna close the conversation here, as we started planning v2.0.0.alpha1. Roadmap for v2.0.0.alpha1