Getting started
Carton splits an app into gem-sized or domain-sized units called Cartons. Each Carton runs in its own Ruby::Box, so its constants, gems, and monkey patches stay inside, and only what it exports crosses the boundary.
Requirements
Section titled “Requirements”- Ruby 4.0.7 or later, run with Boxes enabled:
RUBY_BOX=1 - Bundler 4.0.23 or later, if you use Bundler
Install
Section titled “Install”gem install cartonOr add it to your Gemfile:
# Gemfilegem 'carton'Write a Carton
Section titled “Write a Carton”A Carton is an Entrypoint file plus the directory named after it. The Entrypoint loads the Carton’s own files and exports one value, usually its root module:
billing.rb # the Entrypointbilling/invoice.rb # the Interior: inside the billing Carton# billing/invoice.rb: inside the billing Cartonmodule Billing class Invoice def initialize(total) = @total = total endend# billing.rb: the Entrypoint, which draws the boundaryrequire_relative 'billing/invoice'
module Billing def self.invoice(total) = Invoice.new(total)end
export Billing # the only value that leaves this CartonImport it
Section titled “Import it”# main.rb: the application, in the Main Boxrequire 'carton'
Billing = import_relative 'billing' # runs billing.rb in its own BoxBilling.invoice(42)Run it with Boxes enabled:
RUBY_BOX=1 ruby main.rbimport_relative returns the exported value itself, and defines nothing in the importer. Bind it to a constant of the same name.
What the boundary does
Section titled “What the boundary does”import_relative 'billing/invoice'raisesCarton::ImportError: the file is in the billing Carton’s Interior, so onlybillingcan be imported.- Each Carton loads once per process. Every later import returns the same value, from the app or from another Carton.
import 'billing'searches$LOAD_PATHlikerequire, so gems and path gems import by name.
