Hanami uses the Zeitwerk code loader to support autoloading.

This means that you do not need to require the classes and modules you write before you use them. Instead, your classes and modules are automatically available across your application.

For example, the Bookshelf::Entities::Book class defined in the file app/entities/book.rb can be used in another class by simply using the constant Bookshelf::Entities::Book.

  1. # app/entities/book.rb
  2. module Bookshelf
  3. module Entities
  4. class Book
  5. attr_reader :title, :author
  6. def initialize(title:, author:)
  7. @title = title
  8. @author = author
  9. end
  10. end
  11. end
  12. end

While this is convenient, it does mean you must adhere to Zeitwerk’s expectations around file structure, in which file paths match constant paths.

If class Book was changed to class Novel in the above file, the following error would be raised:

  1. Zeitwerk::NameError: expected file bookshelf/app/entities/book.rb to define constant Bookshelf::Entities::Book, but didnt

Moving the file from app/entities/book.rb to app/entities/novel.rb would address this error.

Autoloading in the app directory

The app/ directory is where you’ll put the majority of your application’s code.

When namespacing classes and modules in your app/ directory, use a top-level module namespace named after your app.

Assuming an app created via hanami new bookshelf (which would have a top-level module Bookshelf), this means abiding by the following structure:

FilenameExpected class or module
app/entities/book.rbBookshelf::Entities::Book
app/entities/author.rbBookshelf::Entities::Author
app/actions/books/create.rbBookshelf::Actions::Books::Create
app/books/operations/create.rbBookshelf::Books::Operations::Create
app/book_binder.rbBookshelf::BookBinder

None of the above classes or modules need a require statement before use.

It’s worth noting that, thanks to Hanami’s component managment system, the components you write in app/ don’t commonly need to reference their collaborators using Ruby constants - they instead use the Deps mixin to access their dependencies.

If you are adding a class to the app/ directory that you want to use an autoloaded Ruby constant to reference, it’s very likely that you do not want that class to be registered in your app container. To opt out of registration, use the magic comment # auto_register: false or one of the alternative methods discussed in “Opting out of the container” in the container and components guide.

  1. # auto_register: false
  2. require "dry-struct"
  3. module Bookshelf
  4. module Structs
  5. class Book < Dry::Struct
  6. attribute :title, Types::String
  7. attribute :author, Types::String
  8. end
  9. end
  10. end

Autoloading in the lib directory

Code placed in lib/bookshelf (i.e. lib/<app_name>) does not need to be required.

This SlackNotifier class from lib/bookshelf for instance can be used in app components without a require statement:

  1. # lib/bookshelf/slack_notifier.rb
  2. module Bookshelf
  3. class SlackNotifier
  4. def self.notify(message)
  5. # ...
  6. end
  7. end
  8. end

However, code placed in other directories within lib/ does need a require statement. Using code from these directories is akin to using a Ruby gem, and so a require statement is necessary.

The custom redis client below, for example, needs to be required (via require "custom_redis/client") when being used:

  1. # lib/custom_redis/client.rb
  2. module CustomRedis
  3. class Client
  4. end
  5. end
  1. # config/providers/redis.rb
  2. Hanami.app.register_provider :redis do
  3. start do
  4. require "custom_redis/client"
  5. redis = CustomRedis::Client.new(url: target["settings"].redis_url)
  6. register "redis", redis
  7. end
  8. end
Constant locationUsage
lib/bookshelf/slack_notifier.rbBookshelf::SlackNotifier
lib/custom_redis/client.rbrequire “custom_redis/client”

CustomRedis::Client

Requiring gems

Autoloading does not apply to any Ruby gems you include in your project via its Gemfile. Like in any regular Ruby project, require external gems before using their constants in each file.

  1. # Gemfile
  2. gem "kramdown", "~> 2.4"
  1. require "kramdown"
  2. module Bookshelf
  3. class Markdown
  4. def to_html(markdown)
  5. Kramdown::Document.new(markdown).to_html
  6. end
  7. end
  8. end

Inflections

If you need to configure acronyms like “DB” or “WNBA”, add them to the infector configuration in the app class:

  1. # config/app.rb
  2. require "hanami"
  3. module Bookshelf
  4. class App < Hanami::App
  5. config.inflections do |inflections|
  6. inflections.acronym "DB", "WNBA"
  7. end
  8. end
  9. end

Autoloading will now expect constants like Bookshelf::DB::Connection instead of Bookshelf::Db::Connection.