module Zeitwerk::Loader::Config
def actual_roots
def actual_roots reject do |root_dir, _root_namespace| ?(root_dir) || ignored_path?(root_dir)
def collapse(*glob_patterns)
Configure directories or glob patterns to be collapsed.
def collapse(*glob_patterns) glob_patterns = expand_paths(glob_patterns) mutex.synchronize do collapse_glob_patterns.merge(glob_patterns) collapse_dirs.merge(expand_glob_patterns(glob_patterns)) end end
def collapse?(dir)
def collapse?(dir) se_dirs.member?(dir)
def dirs(namespaces: false, ignored: false)
These are read-only collections, please add to them with `push_dir`.
If `ignored` is falsey (default), ignored root directories are filtered out.
values are their corresponding namespaces, class or module objects.
instead. Keys are the absolute paths of the root directories as strings,
paths of the root directories as strings. If truthy, returns a hash table
If `namespaces` is falsey (default), returns an array with the absolute
def dirs(namespaces: false, ignored: false) if namespaces if ignored || ignored_paths.empty? roots.clone else roots.reject { |root_dir, _namespace| ignored_path?(root_dir) } end else if ignored || ignored_paths.empty? roots.keys else roots.keys.reject { |root_dir| ignored_path?(root_dir) } end end.freeze end
def do_not_eager_load(*paths)
in those files are still autoloadable.
Let eager load ignore the given files or directories. The constants defined
def do_not_eager_load(*paths) mutex.synchronize { eager_load_exclusions.merge(expand_paths(paths)) } end
def enable_reloading
There is no way to undo this, either you want to reload or you don't.
You need to call this method before setup in order to be able to reload.
def enable_reloading mutex.synchronize do break if @reloading_enabled if @setup raise Zeitwerk::Error, "cannot enable reloading after setup" else @reloading_enabled = true end end end
def excluded_from_eager_load?(abspath)
def excluded_from_eager_load?(abspath) mize this common use case. false if eager_load_exclusions.empty? p(abspath) do |path| rn true if eager_load_exclusions.member?(path) rn false if roots.key?(path)
def expand_glob_patterns(glob_patterns)
def expand_glob_patterns(glob_patterns) that Dir.glob works with regular file names just fine. That is, patterns technically need no wildcards. atterns.flat_map { |glob_pattern| Dir.glob(glob_pattern) }
def expand_paths(paths)
def expand_paths(paths) flatten.map! { |path| File.expand_path(path) }
def ignore(*glob_patterns)
Configure files, directories, or glob patterns to be totally ignored.
def ignore(*glob_patterns) glob_patterns = expand_paths(glob_patterns) mutex.synchronize do ignored_glob_patterns.merge(glob_patterns) ignored_paths.merge(expand_glob_patterns(glob_patterns)) end end
def ignored_path?(abspath)
def ignored_path?(abspath) d_paths.member?(abspath)
def ignores?(abspath)
descendant of an ignored directory.
Returns true if the argument has been configured to be ignored, or is a
def ignores?(abspath) n use case. false if ignored_paths.empty? (abspath) do |path| n true if ignored_path?(path) n false if roots.key?(path)
def initialize
def initialize @inflector = Zeitwerk::Inflector.new @logger = self.class.default_logger @tag = SecureRandom.hex(3) @initialized_at = Time.now @roots = {} @ignored_glob_patterns = Set.new @ignored_paths = Set.new @collapse_glob_patterns = Set.new @collapse_dirs = Set.new @eager_load_exclusions = Set.new @reloading_enabled = false @on_setup_callbacks = [] @on_load_callbacks = {} @on_unload_callbacks = {} end
def log!
Logs to `$stdout`, handy shortcut for debugging.
def log! @logger = ->(msg) { puts msg } end
def on_load(cpath = :ANY, &block)
end
# ...
loader.on_load do |cpath, value, abspath|
Can also be configured for any constant loaded:
end
klass.endpoint = "https://api.dev"
loader.on_load("SomeApiClient") do |klass, _abspath|
the order in which they were defined.
Supports multiple callbacks, and if there are many, they are executed in
Configure a block to be invoked once a certain constant path is loaded.
def on_load(cpath = :ANY, &block) raise TypeError, "on_load only accepts strings" unless cpath.is_a?(String) || cpath == :ANY mutex.synchronize do (on_load_callbacks[cpath] ||= []) << block end end
def on_setup(&block)
If setup was already done, the block runs immediately.
Configure a block to be called after setup and on each reload.
def on_setup(&block) mutex.synchronize do on_setup_callbacks << block block.call if @setup end end
def on_unload(cpath = :ANY, &block)
end
# ...
loader.on_unload do |cpath, value, abspath|
Can also be configured for any removed constant:
end
klass.clear_cache
loader.on_unload("Country") do |klass, _abspath|
order in which they were defined.
Supports multiple callbacks, and if there are many, they are executed in the
Configure a block to be invoked right before a certain constant is removed.
def on_unload(cpath = :ANY, &block) raise TypeError, "on_unload only accepts strings" unless cpath.is_a?(String) || cpath == :ANY mutex.synchronize do (on_unload_callbacks[cpath] ||= []) << block end end
def push_dir(path, namespace: Object)
descendants.
the same process already manages that directory or one of its ascendants or
Raises `Zeitwerk::Error` if `path` does not exist, or if another loader in
Pushes `path` to the list of root directories.
def push_dir(path, namespace: Object) unless namespace.is_a?(Module) # Note that Class < Module. raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module object, should be" end unless real_mod_name(namespace) raise Zeitwerk::Error, "root namespaces cannot be anonymous" end abspath = File.expand_path(path) if dir?(abspath) raise_if_conflicting_directory(abspath) roots[abspath] = namespace else raise Zeitwerk::Error, "the root directory #{abspath} does not exist" end end
def recompute_collapse_dirs
def recompute_collapse_dirs se_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
def recompute_ignored_paths
def recompute_ignored_paths d_paths.replace(expand_glob_patterns(ignored_glob_patterns))
def reloading_enabled?
def reloading_enabled? @reloading_enabled end
def root_dir?(dir)
def root_dir?(dir) key?(dir)
def tag
writer below.
Implemented as a method instead of via attr_reader for symmetry with the
Returns the loader's tag.
def tag @tag end
def tag=(tag)
Sets a tag for the loader, useful for logging.
def tag=(tag) @tag = tag.to_s end