V2.2: Booting
Hanami applications support a prepared state and a booted state.
Whether your app is prepared or booted determines whether its components are lazily loaded on demand (for a prepared app), or eagerly loaded up front (for a booted app).
This difference may not sound like much, but the ability of a prepared app to load just enough of the app to undertake a particular task can be a huge performance advantage when it comes to testing, worker processes and other use cases where your app doesn’t need every component to perform the job at hand. A rake task that precompiles assets or migrates the database can execute quickly when is not delayed by loading the entire application.
Conversely, a booted app is fully initialized, with every component loaded and ready to do its work. This is ideal whenever you want to incur initialization costs at boot time, such as when serving web requests.
Hanami.prepare
When you call Hanami.prepare
(or use require "hanami/prepare"
), Hanami will prepare your app for use.
This process makes the Ruby source files in app/
autoloadable and makes your components (from the same classes defined in app/
) lazily loadable.
This approach keeps load time to a minimum. As such, it’s the default mode in the Hanami console and when running tests.
A prepared application will do everything a booted one can, it will just lazily load only the components it needs to perform a particular task on demand.
Hanami.boot
When you call Hanami.boot
(or use require "hanami/boot"
) Hanami will do everything it does when Hanami.prepare
is called, but then go further, starting each of your app’s providers, while also eagerly loading all of your app’s components up front.
This is useful when you want to incur all initialization costs at boot time.
Booting is the approach taken in Hanami’s standard Puma setup. Thus, in Hanami’s config.ru
file you will see:
require "hanami/boot"
run Hanami.app
Stepping through the boot process
Purely as an exercise, we can explore both preparing and booting by starting irb
in a directory containing a Hanami project. This is not something that’s needed in day to day development with Hanami - it’s useful here as a demonstration of the booting behaviour.
Assuming we have run hanami new bookshelf
to generate a new app (see Getting started for a full guide to creating your first Hanami application), let’s create a hello world component in app/hello_world.rb
:
# app/hello_world.rb
module Bookshelf
class HelloWorld
def say_hello
"Hello world!"
end
end
end
And now, in our project directory, let’s run irb
:
bundle exec irb
irb(main)>
Typing Hanami
here will result in a NameError
:
irb(main)> Hanami
(irb):1:in `<main>': uninitialized constant Hanami (NameError)
Let’s address that by running require "hanami"
.
Once the Hanami module is available, calling Hanami.setup
will load the Hanami app defined in config/app.rb
:
irb(main)> require "hanami"
=> true
irb(main)> Hanami.setup
=> Bookshelf::App
irb(main)> Hanami.app
=> Bookshelf::App
We can now ask our app whether it’s prepared or booted:
irb(main)> Hanami.app.prepared?
=> false
irb(main)> Hanami.app.booted?
=> false
We can also see what components are registered with the app by calling #keys
:
irb(main)> Hanami.app.keys
=> []
This makes sense, as the app hasn’t even been prepared yet, meaning component registration hasn’t begun!
Let’s prepare the app now, then ask what component keys are registered:
irb(main)> Hanami.prepare
=> Bookshelf::App
irb(main)> Hanami.app.prepared?
=> true
irb(main)> Hanami.app.keys
=> ["settings", "notifications"]
Two components are now present: settings
and notifications
.
notifications
is an instance of Dry::Monitor::Notifications
that’s registered early in the prepare process to support inter-component notifications (for now mostly a framework-internal concern).
The settings
component has been loaded in order to ensure that necessary settings are present. If a setting wasn’t satisfied, the app would have raised an invalid settings error at this point.
You’ll notice that our hello world component does not appear under the key "hello_world"
. If we called Hanami.boot
at this point, the eager component loading would register it for us.
Rather than do that though, let’s see what happens if we just try to use our component.
irb(main):011:0> Hanami.app["hello_world"].say_hello
=> "Hello world!"
Success! Even though the component wasn’t yet registered, it was lazily loaded when we used it!
If we now check what components have been registered in the app container, we’ll see "hello_world"
.
irb(main)> Hanami.app.keys
=> ["settings", "notifications", "hello_world"]
From here, calling Hanami.boot
will register the remainder of our application’s components, which in this case are just the components registered by Hanami’s default providers: "routes"
, "inflector"
, "logger"
and "rack.monitor"
.
irb(main)> Hanami.boot
=> Bookshelf::App
irb(main)> Hanami.app.keys
["settings",
"notifications",
"hello_world",
"routes",
"inflector",
"logger",
"rack.monitor"]
You can read more about components and containers in more detail in the container and components guide. Providers are covered in the providers guide.