Hanami 2.0 Ideas


#4

Improve CLI commands

Our implementation is based on thor, which doesn’t work well with subcommands. Aside from that, the implementation of the generators is hard to maintain. Luckily we have a really good integration testing coverage so we can apply the following ideas.

Switch from thor to optparse

As mentioned above, thor doesn’t work well with subcommands and with exit codes. We can replace it with optparse from Ruby’s standard library.

thor ships with useful file manipulation utilities. We can promote our file helpers from our test suite as replacement.

Our helpers don’t provide a consistent indentation of Ruby code. We should find a solution for that (https://rubygems.org/gems/rubyfmt ?).

Commands as objects

thor demands that commands are methods of an object.

class MyCli < Thor
  def command_a
  end
end

We want to Implement each (sub)command as an object. This approach allows us to put args validations in each class and it decouples the command (hanami routes) from the concrete implementation that can be provided by us or a third party gem.

class MyCommand < Hanami::Command
  # arguments validations DSL

  def call(arguments)
    # do something
  end
end

Let commands to register themselves

class MyCommand < Hanami::Command
  # hanami routes
  register_as "routes"

  # or ...

  # hanami generate auth
  register_as "auth", subcommand_of: "generate"

  # ...
end

This opens an endless possibilities for our ecosystem.

Please note that a third party-gem can take over an entire (sub)command (eg. hanami-cool-orm can take over hanami generate model)

Commands dependencies

There are commands that don’t need the full blown Hanami project to do their job.
Think of hanami routes, it only needs the routes from the project, without connecting to the database.

As of 0.9, we already let some commands to pick that dependencies. The goal is to let all the commands to work with this approach. See this example: https://github.com/hanami/hanami/blob/master/lib/hanami/commands/routes.rb


These features can be shipped in the 1.x series, as they don’t introduce breaking changes for our users.


#5

Improve Code Reloading

We use shotgun as a soft-dependency for code reloading. If required in Gemfile, hanami server auto-detects it and the code is auto-reloaded after each incoming HTTP request.

We broke code reloading a few times, and the only way to fix it was to introduce Hanami::Utils.reload! in order to load again the files under lib/.

We can investigate alternatives for shotgun and integrate them:

  • guard my favorite, it comes with a lot of useful plugins like guard-livereload and it works on Windows too.
  • rerun it works out-of-the-box, with less configurations. It works only on *NIX.
  • entr this is a *NIX executable written in C. We introduced experimental support for it from 0.8 to 0.9 and didn’t worked well. But some people in our chat are promoting it, so we can have a second look.

The goal is still the same: keep Hanami project unaware of code reloading. Only hanami server is aware of this feature, and it acts as a reloadable shell for a project.


The target release for this has to be decided. There are some solutions like rerun and entr that don’t require changes in existing projects, while guard requires the addition of a Guardfile.

Depending on the decision that we’ll take and the effort it can be in 1.x series or even in 2.0.


#6

Native Webpack Support

I confessed several times, I’m a backend guy. Hanami assets as they are now, work fine for my needs. However, we received several feedbacks about how this system is limiting and how Webpack is better for advanced features. No doubt it is. So why don’t let frontent devs to work with their favorite tool?

By default, a new project will have the actual assets system (hanami new bookshelf).
But when the --webpack flag, it generates the configurations needed by Webpack.

There are a few technical issues to address:

  • How to hook Webpack paths for assets with our helpers (eg. javascript)?
  • We should provide a smooth development experience to start both Hanami and Webpack server. My idea is to use foreman and a Procfile.dev, and when hanami server is started, it will use it.

This will probably need to introduce breaking change, so the target release is 2.0. I want a frontend dev to work/advise on this.


#7

Unobtrusive JS (UJS)

The idea is to use vanilla-ujs gem to provide AJAX capabilities to Hanami, without depending on a JS framework.

This is a two steps improvement:

  • Bundle vanilla-ujs files to make them available into a Hanami project
  • Expand the capabilities of our helpers (eg. form_for, link_to).

This can theoretically be part of the 1.x series.


#8

New Router Implementation

hanami-router uses http_router as engine. This gem served well us until now, but it’s no longer maintained. We want to switch to roda .


This will require breaking changes in the routing API, so the target release is 2.0.


#9

Events/Instrumentation

Introduce an events system that can provide an API to emit/handle them. The system is similar to ActiveSupport::Notifications, and it should use wisper gem.

The idea is to “broadcast” an event happening in the framework or in a Hanami project and let the other part of the system to react to it. There are several scenarios.

Pub/Sub

class WelcomeMailer
  subscribe_to "user.signup"
end
class DripAdapter
  subscribe_to "user.signup"
end
class Signup
  include Hanami::Interactor

  def call(data)
    user = UserRepository.new.create(data[:user])
    broadcast("user.signup", user: user)
  end
end

When that event is emitted, WelcomeMailer and DripAdapter will receive a notification and react accordingly.

Async Execution

Continuing on the previous example, the broadcast is immediate, but the execution of the receiver logic can be async. We can send to a queue system (eg. sidekiq) the name of the event and the payload and let it to schedule the execution.

The API could be

broadcast("user.signup", user: user).async(...)

Instrumentation

def render
  broadcast('rendering.template') do
    # ...
  end
end

This reports the elapsed time. We can use it to log when a template was rendered.


This can be part of 1.x series.


#10

Move Mailers To apps/

Hanami architecture has one golden rule: the core of a Hanami project lives in lib/, while the deliverability mechanisms (for the web) live in apps/. The code in apps/ can reference (depend) code in lib/ but NOT viceversa.

Because we offer Interactors (aka service objects) as part of the core of a Hanami project, their natural place is lib/.

Interactors sometimes need to send emails, so to not break the golden rule above, we put mailers in lib/ too.

This is wrong for two reasons:

  • Mailers are deliverability mechanisms too, so they shouldn’t stay in lib/
  • If they stay out of apps/ they cannot access to assets helpers

The solution is to move mailers to apps/, and so the command to generate a mailer should go from:

hanami generate mailer welcome

to:

hanami generate mailer web welcome

Where web is one of the applications living under apps/. This is the same concept of hanami generate action command.


Because this is a breaking change the target release is 2.0.


#11

I think we can ask help from Samuel Simões (https://github.com/samuelsimoes) for this


#12

Great, do you know Samuel?


#13

@jodosha Do you think it would be useful to have something like the route middlewares from Laravel or it doesn’t match with the framework?

Example

group admin: AdminMiddleware do
  resources 'books'
end

And have an AdminMiddleware called before every route on the group.


#14

Nope, but I think it will not a problem for us :slight_smile:
We can send an email or tweet him.


#15

What’s the advantage of this? Do you know you can add a middleware in front of single apps as now? Thanks.


#16

Ok, but why him then?


#17

because he is the author of https://github.com/samuelsimoes/hanami-webpack and I think he have experience in this question


#18

I didn’t know that, can you give me some pointers please?


#19

@CassioGodinho please open apps/web/application.rb and search for middleware.use, it’s commented code.


#20

One advantage I can think of is having a Middleware for only some routes on an app that needs to be authenticated and halting the request before it gets to the controller eliminating the need of a before :authenticate!.

Maybe I’m just trying to find a way to work the way I’m used to work on Laravel and I have yet to understand how things work with Hanami.


#21

First of all, I’m very happy to hear that Hanami team is considering Webpack. Webpack is an awesome tool to bundle/process assets.

I’ve created the hanami-webpack, a very experimental and hacky gem to run Webpack with Hanami, but it’s important to tell that I just followed some principles that I saw in https://github.com/mipearson/webpack-rails.

The answers to your questions:

In my gem I used the Webpack’s StatsPlugin to expose, through a JSON manifest, the path for my [webpack entry points] (http://webpack.github.io/docs/configuration.html#entry), so the Hanami can download this manifest in order to figure out the paths for the entries.

Here some key points about the implementation:

About the server, I totally agree with you about the smooth integration between webpack dev server and Hanami, in my gem I made a monkey patch on the Hanami::DevServer.start to spawn a process with the webpack dev server https://github.com/samuelsimoes/hanami-webpack/blob/master/lib/hanami_webpack/dev_server.rb#L7 to make the dev server integration transparent, I don’t know if it’s a good idea, just sounded good for me.

I’m not a specialist in Webpack or Hanami, but I’m glad to help you guys on this topic. :smiley:


#22

@samuelsimoes Thanks for joining this conversation. Do you use hanami-webpack in production?


#23

@CassioGodinho

You can mount middleware at action level, or you can share a module across all the actions, so #authenticate! is always called, unless you want to skip it.