Hello wonderful community! ❤️🌸
Today we're thrilled to announce v2.0.0.alpha1
release 🙌.
A fresh start 😎
Today's release is the beginning of Hanami 2 series. The final 2.0 version will be released later this year.
We decided to start fresh with the development of the framework. The internals needed a cleanup because the code was accumulating techical debt from the old days of Lotus. To evolve it would have required too much effort. Given 1.3 is 4k lines of code, it made a lot of sense for us to start from scratch.
That means this release is not meant to be comparable in terms of features with 1.3, but it's more a preview of Hanami 2.
Application
DRY-up settings
The big change from Hanami 1 is the removal of all the applications (web
, admin
) as a way to micro-configure areas of your project.
In the past we had:
config/enviroment.rb
apps/{web,admin,...}/application.rb
To configure where to load files from, which was the default response format for each single app, the sessions, cookies, base URL, just to name a few.
The concept is to unify all these settings at the level of config/application.rb
, so they are propagated to the apps.
DRY at its finest.
Here's an example of config/application.rb
:
# frozen_string_literal: true
module Soundeck
class Application < Hanami::Application
config.cookies = { max_age: 600 }
config.sessions = :cookie, { secret: ENV["SOUNDECK_SESSIONS_SECRET"] }
config.middleware.use Middleware::Elapsed, "1.0"
config.environment(:production) do |c|
c.base_url = ENV["SOUNDECK_BASE_URL"]
c.logger = { level: :info, formatter: :json }
end
end
end
Configuration
We introduced a new syntax for the application configuration based on getters and setters, instead of the DSL we had in Hanami 1.
Before
cookies max_age: 600
After
config.cookies = { max_age: 600 }
This new syntax helps to simplify the internals, by explicitly separating the intent of reading a value from the intent of setting it.
Hanami::Configuration
now performs atomic/thread-safe operations both at the read and write time.
Defaults
Another difference from the past is that configuration now ships with defaults, instead of making everything explicit in the generated app.
Let me give you an example:
Before
This was an explicit setting of each application configuration.
# apps/web/application.rb
security.x_frame_options = "DENY"
After
Now the configuration has that value as a default, so the generated code is empty:
In case a developer needs to change the default, they will do it for the whole app:
# config/application.rb
config.security.x_frame_options = "sameorigin"
Router
hanami-router
has been rewritten more than a year ago. It's more efficient (+10k req/s for simple Rack benchmark) and it's now based on mustermann
path matchers, which powers Sinatra routing as well.
In Hanami 1 we had several places to configure the whole routes of an app: apps/{web,admin,...}/config/routes.rb
.
Now these files are gone in favor of config/routes.rb
at the root of the project.
This change has an impact both on simplification and performance.
Thanks to hanami-router
2, we can define all the routes for all the apps in a single shot in config/routes.rb
:
# frozen_string_literal: true
Hanami.application.routes do
mount :web, at: "/" do
root to: "home#index"
end
mount :admin, at: "/admin" do
root to: "home#index"
end
end
Please note the differences from the past:
- No more multiple files and multiple routers instances for multiple apps
- Single router for the whole app 🙌
- Mounting apps is no longer delegated to the gone
Hanami.configure
, which was clutteringconfig/environment.rb
- Apps as concrete classes are gone (e.g.
Web::Application
), in favor of symbols - The apps registered with
mount
are the ones that are activated in the internal container
Actions
Actions are been redesigned to improve their performance.
In Hanami 1, for each incoming HTTP request, we had to create a new action instance, because assigning instance variables (e.g. @book
) and using them as exposures, we had to re-create a blank state, with a new action.
In the long run, that was putting pressure on Ruby garbage collector.
To remediate to this problem, we made the actions immutable. In order to pass the data from the action to the view, you can use:
response[:book] = Book.new
This also eliminates the need to declare data via the expose
DSL, which is now gone.
Another important change from the past is that Hanami::Action
is now a superclass to inherit from.
In your app you will have a base action class for each sub application (e.g. apps/web/action.rb
):
# frozen_string_literal: true
module Web
class Action < Hanami::Action
end
end
This base class is designed to share code with its subclasses.
For instance, the action at apps/web/actions/books/show.rb
will look like this:
# frozen_string_literal: true
module Web
module Actions
module Books
class Show < Web::Action
def handle(req, res)
res[:book] = BookRepository.new.find(req.params[:id])
end
end
end
end
end
Code reloading
We extracted code reloading from hanami
core to hanami-reloader
.
The new implementation is based on guard-rack
: this gem watches the file system and restarts the server when it detects a file change.
This makes code reloading an external feature of the framework, no longer builtin in its core.
The previous builtin implementation based on shotgun
was causing compatibility problems (JRuby, Windows, better_errors
gem), and it was source of bugs.
The feature is still integrated with hanami server
(use --no-code-reloading
to skip).
The interesting bit is that, if hanami-reloader
isn't installed, hanami server
won't use code reloading, because it isn't implemented by Hanami.
This is useful in production, where you don't need code reloading.
CLI commands
Because this is the first preview of 2.0, only two CLI commands are available for now:
hanami version
hanami server
We'll re-add all the usual CLI commands from 1.3, as soon we'll implement the related features.
Released Gems 💎
hanami-2.0.0.alpha1
hanami-cli-1.0.0.alpha1
hamami-controller-2.0.0.alpha1
hanami-router-2.0.0.alpha1
hanami-utils-2.0.0.alpha1
hanami-reloader-1.0.0.alpha1
How to try it ⌨️
$ gem install hanami --pre
Please check out https://github.com/jodosha/soundeck if you want to see a working example.
What's next? ⏰
We'll release a new alpha version, in April 2019.
Happy coding! 🌸