Documentation
¶
Overview ¶
Package zeitwerk is a pure-Go (no cgo) model of the engine of Ruby's Zeitwerk autoloader, faithful to the observable behaviour of the zeitwerk gem's default configuration on MRI 4.0.5.
It reimplements the part of Zeitwerk::Loader that is pure logic — scanning the managed directory trees and computing the bidirectional mapping between constant paths (e.g. "Admin::UsersController") and the file or directory that defines them, honouring namespaces, collapsed directories, and ignored paths, and driving the setup / eager-load / reload / unload lifecycle and its callbacks. Everything that actually touches a Ruby runtime — defining a constant, requiring a file, removing a constant — is left to the host through small function seams (DefineAutoload, Load, and the on_unload callback), so this package has no dependency on any Ruby runtime and is the reusable engine a go-embedded-ruby binding plugs into.
Index ¶
- type Autoload
- type DefineAutoloadFunc
- type DirEntry
- type Error
- type FS
- type Inflector
- type LoadFunc
- type Loader
- func (l *Loader) Autoloads() []Autoload
- func (l *Loader) Collapse(globs ...string)
- func (l *Loader) CpathAt(filePath string) (Autoload, bool)
- func (l *Loader) EagerLoad() error
- func (l *Loader) EnableReloading()
- func (l *Loader) Ignore(globs ...string)
- func (l *Loader) Inflector() *Inflector
- func (l *Loader) OnLoad(cpath string, fn func(cpath, filePath string))
- func (l *Loader) OnSetup(fn func())
- func (l *Loader) OnUnload(fn func(cpath, filePath string))
- func (l *Loader) PathAt(cpath string) (Autoload, bool)
- func (l *Loader) PushDir(dir, namespace string) error
- func (l *Loader) Reload() error
- func (l *Loader) SetDefineAutoload(fn DefineAutoloadFunc)
- func (l *Loader) SetFS(fs FS)
- func (l *Loader) SetInflector(in *Inflector)
- func (l *Loader) SetLoad(fn LoadFunc)
- func (l *Loader) Setup() error
- func (l *Loader) Unload()
- type NameError
- type SetupRequired
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Autoload ¶
type Autoload struct {
Cpath string // fully-qualified constant path, e.g. "Admin::UsersController"
Cname string // final segment only, e.g. "UsersController"
Path string // absolute file or directory path that defines it
IsDir bool // true for an implicit namespace module (a managed directory)
}
Autoload is one entry of the computed constant map: the constant a managed path defines.
type DefineAutoloadFunc ¶
DefineAutoloadFunc is the seam the host uses to register an autoload, standing in for Ruby's `Module#autoload`. Setup calls it once per managed constant with the fully-qualified constant path and the file or directory that defines it (isDir true for an implicit namespace module autovivified from a directory). It is optional; a nil seam means "engine only", useful for tests that just assert the computed map.
type DirEntry ¶
DirEntry is one child of a directory as reported by an FS: its basename and whether it is itself a directory. It is the minimal shape the loader's directory scan needs.
type Error ¶
type Error struct{ Msg string }
Error is the base error type of the loader, mirroring Zeitwerk::Error (a StandardError subclass in the gem). PushDir on a directory that does not exist, and Reload without reloading enabled, both surface as an *Error, just as the gem raises Zeitwerk::Error for those conditions.
type FS ¶
type FS interface {
// ReadDir lists the children of dir. The returned entries need not be
// sorted; the loader orders constants deterministically itself.
ReadDir(dir string) ([]DirEntry, error)
}
FS is the filesystem seam the loader scans. The default implementation reads the real filesystem, so PushDir takes ordinary absolute paths; tests (and any host that manages a virtual tree) inject their own by calling SetFS. Only directory listing is required — the loader never reads file contents itself, since loading a file is the host's Load seam.
type Inflector ¶
type Inflector struct {
// contains filtered or unexported fields
}
Inflector models Zeitwerk::Inflector, the default inflector that maps a file or directory basename to the constant name it defines. It is a faithful port of the gem's default_inflector: a very basic snake_case -> CamelCase conversion, honouring hard-coded overrides configured with Inflect.
The reference Ruby is:
def camelize(basename, _abspath)
inflections[basename] || basename.split("_").each_with_object("".dup) do |word, camelized|
camelized << (inflections[word] || word[0].upcase << word[1..-1])
end
end
So Camelize first consults a whole-basename override, and otherwise splits on "_" and, for each word, consults a per-word override or upcases just the first character while leaving the rest of the word untouched. This is why the default turns "html_parser" into "HtmlParser" (not "HTMLParser") until an acronym override is registered.
func NewInflector ¶
func NewInflector() *Inflector
NewInflector returns a default Zeitwerk inflector with no overrides.
func (*Inflector) Camelize ¶
Camelize maps a snake_case basename to the constant name it defines, applying the same rules as Zeitwerk::Inflector#camelize. The abspath argument the gem accepts is not needed by the default inflector and is therefore omitted.
func (*Inflector) Inflect ¶
Inflect registers hard-coded basename/word -> constant-name overrides, mirroring Zeitwerk::Inflector#inflect. Keys may be whole basenames ("html_parser" => "HTMLParser") or individual words ("html" => "HTML"); both are consulted by Camelize. Later calls merge into and override earlier ones.
type LoadFunc ¶
LoadFunc is the seam the host uses to actually load a managed file, standing in for Ruby's `require`. EagerLoad calls it once per managed file; returning a non-nil error aborts the eager load with that error (the host may return a *NameError when the file does not define its expected constant).
type Loader ¶
type Loader struct {
// contains filtered or unexported fields
}
Loader models Zeitwerk::Loader: a registry of root directories and the constant map computed from them. The zero value is not usable; construct one with NewLoader.
func NewLoader ¶
func NewLoader() *Loader
NewLoader returns a loader with the default Zeitwerk inflector and the real filesystem. Configure it with PushDir / Ignore / Collapse and the seams, then call Setup.
func (*Loader) Autoloads ¶
Autoloads returns the computed constant map as a slice sorted by constant path, a stable snapshot suitable for assertions and for a host that wants to enumerate the managed constants.
func (*Loader) Collapse ¶
Collapse registers glob patterns of directories that do not represent a namespace: a collapsed directory's children are promoted into its parent namespace, mirroring `Zeitwerk::Loader#collapse`. Matching uses the same path.Match semantics as Ignore.
func (*Loader) CpathAt ¶
CpathAt returns the constant a given file or directory path defines, the inverse direction of the map (path -> constant). The boolean is false if the path is not managed.
func (*Loader) EagerLoad ¶
EagerLoad loads every managed file and fires the on_load callbacks for every managed constant, mirroring `Zeitwerk::Loader#eager_load`. Constants are processed in sorted order so parent namespaces precede their children. It returns a *SetupRequired if Setup has not run, and propagates the first error the Load seam returns.
func (*Loader) EnableReloading ¶
func (l *Loader) EnableReloading()
EnableReloading permits Reload, mirroring `Zeitwerk::Loader#enable_reloading`. It must be called before Setup; Reload on a loader without it returns an *Error, as the gem raises Zeitwerk::Error.
func (*Loader) Ignore ¶
Ignore registers glob patterns whose matching files and directories are excluded from the managed tree, mirroring `Zeitwerk::Loader#ignore`. Patterns are matched against absolute paths with path.Match semantics (a pattern with no metacharacters matches that exact path).
func (*Loader) Inflector ¶
Inflector returns the loader's inflector so callers can register overrides, e.g. Loader.Inflector().Inflect(map[string]string{"html_parser": "HTMLParser"}).
func (*Loader) OnLoad ¶
OnLoad registers a callback fired when a managed constant is loaded (during EagerLoad here, since this engine has no lazy autoload trigger of its own), mirroring `Zeitwerk::Loader#on_load`. An empty cpath (or "ANY") fires for every constant; a specific cpath fires only for that one. This matches the gem, where `on_load(:ANY)` runs for all and `on_load("Foo")` for just Foo.
func (*Loader) OnSetup ¶
func (l *Loader) OnSetup(fn func())
OnSetup registers a callback fired at the end of every Setup (and thus after each Reload), mirroring `Zeitwerk::Loader#on_setup`.
func (*Loader) OnUnload ¶
OnUnload registers a callback fired for each managed constant when it is unloaded, mirroring `Zeitwerk::Loader#on_unload`. It is the host's seam for removing the constant, since removal touches the Ruby runtime.
func (*Loader) PathAt ¶
PathAt returns the managed path a given constant path maps to (constant -> path). The boolean is false if the constant is not managed.
func (*Loader) PushDir ¶
PushDir registers a root directory whose children map into namespace, mirroring `Zeitwerk::Loader#push_dir(path, namespace:)`. An empty namespace (or "Object") means the top level. The directory must exist; otherwise PushDir returns an *Error, as the gem raises Zeitwerk::Error for a missing root directory.
func (*Loader) Reload ¶
Reload unloads and sets up again, picking up filesystem changes, mirroring `Zeitwerk::Loader#reload`. It returns an *Error if reloading was not enabled with EnableReloading (the gem raises Zeitwerk::Error), and a *SetupRequired if Setup has not run yet.
func (*Loader) SetDefineAutoload ¶
func (l *Loader) SetDefineAutoload(fn DefineAutoloadFunc)
SetDefineAutoload installs the DefineAutoload seam (see DefineAutoloadFunc).
func (*Loader) SetFS ¶
SetFS replaces the filesystem seam the loader scans (default: the real filesystem).
func (*Loader) SetInflector ¶
SetInflector replaces the inflector, mirroring `Zeitwerk::Loader#inflector=`.
func (*Loader) Setup ¶
Setup scans the registered root directories and builds the constant map, registering each managed constant through the DefineAutoload seam, mirroring `Zeitwerk::Loader#setup`. It then fires the on_setup callbacks. Setup is idempotent: calling it again while already set up is a no-op.
type NameError ¶
type NameError struct{ Msg string }
NameError mirrors Zeitwerk::NameError (a ::NameError subclass in the gem). It models the failure raised when a managed file or directory does not define the constant its path maps to; the host binding raises it from its Load / DefineAutoload seam and it is surfaced here so callers can match on the type.
type SetupRequired ¶
type SetupRequired struct{}
SetupRequired mirrors Zeitwerk::SetupRequired. EagerLoad and Reload return it when the loader has not been set up yet, exactly as the gem raises Zeitwerk::SetupRequired from eager_load and friends before setup has run.
func (*SetupRequired) Error ¶
func (e *SetupRequired) Error() string
