Cartons
A Carton is an Entrypoint file plus the directory named after it, the same shape as a gem’s lib/ and a Zeitwerk explicit namespace:
lib/billing.rb # the Entrypointlib/billing/invoice.rb # inside the billing Cartonlib/billing/models/charge.rb # inside too: models/ has no models.rb- The Entrypoint loads the Carton’s other files with
require,require_relative, or a Zeitwerk loader, all inside the Carton’s one Box. - Only the Entrypoint can be imported. Importing a file inside a Carton raises
Carton::ImportErrornaming the import to use, because loading it on its own would create a second copy of the Carton’s classes. - Directories without their own
.rbfile, like Zeitwerk implicit namespaces, belong to the Carton above them, so the outermost Entrypoint wins. - A file with no directory beside it is a Carton of one file.
- No gemspec, Gemfile, or manifest is required. A Carton may be a gem, a path gem, a gem-shaped directory, or a plain file.
Nested Cartons
Section titled “Nested Cartons”Cartons can nest in another Carton’s tree, as long as they sit outside its Interior. In billing/cartons/tax/lib/tax.rb, tax is its own Carton, which billing.rb can import_relative '../cartons/tax'. Anything under billing/lib/billing/ belongs to billing, so put nested Cartons beside that directory, not in it.
Gem-shaped directories
Section titled “Gem-shaped directories”A directory such as components/billing/ holding lib/billing.rb imports by its path. The import resolves to lib/billing.rb as the Entrypoint and puts lib/ on the Carton’s load path, so the Carton can require 'billing/invoice' as a gem would.
Billing = import_relative '../components/billing' # components/billing/lib/billing.rbZeitwerk
Section titled “Zeitwerk”A Carton’s Entrypoint can run its own Zeitwerk loader, such as Zeitwerk::Loader.for_gem, in its own Box with its own copy of Zeitwerk.
- Constants left to autoload load in the Carton’s Box, even when an importer references them first, and go through the Carton’s Zeitwerk, so
on_loadcallbacks and reloading work. Eager load at boot in production, as usual. - If the app’s own loader manages a directory that contains a Carton,
ignorethe Carton’s Entrypoint and directory there. Otherwise the app’s loader can load the Carton’s files in the Main Box. - Reloading a Carton defines new classes inside it, but importers keep whatever classes and objects they already hold.
- A loader inside a Carton may use nsfiles (Zeitwerk 2.8+,
loader.nsfile = 'ns.rb'), socarriers/ns.rbdefinesCarriers. The Carton itself still needs an Entrypoint beside its directory, such asshipping.rbbesideshipping/.
The zeitwerk example shows each case.
