Module: Zeitwerk::Loader::Config

Extended by:
Internal
Includes:
RealModName
Included in:
Zeitwerk::Loader
Defined in:
lib/zeitwerk/loader/config.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Internal

internal

Methods included from RealModName

#real_mod_name

Instance Attribute Details

#inflectorObject

: camelize(String, String) -> String



14
15
16
# File 'lib/zeitwerk/loader/config.rb', line 14

def inflector
  @inflector
end

#loggerObject

: call(String) -> void | debug(String) -> void | nil



17
18
19
# File 'lib/zeitwerk/loader/config.rb', line 17

def logger
  @logger
end

#nsfileObject

Basename of files that define namespaces. For example, if ‘nsfile` is ’ns.rb’, then ‘foo/ns.rb` defines the `Foo` namespace.

: String?



39
40
41
# File 'lib/zeitwerk/loader/config.rb', line 39

def nsfile
  @nsfile
end

#rootsObject (readonly)

Absolute paths of the root directories, mapped to their respective root namespaces:

'/Users/fxn/blog/app/channels' => Object,
'/Users/fxn/blog/app/adapters' => ActiveJob::QueueAdapters,
...

Stored in a hash to preserve order, easily handle duplicates, and have a fast lookup by directory.

This is a private collection maintained by the loader. The public interface for it is ‘push_dir` and `dirs`.

: Hash[String, Module]



32
33
34
# File 'lib/zeitwerk/loader/config.rb', line 32

def roots
  @roots
end

Instance Method Details

#collapse(*glob_patterns) ⇒ Object

Configure directories or glob patterns to be collapsed.

: (*(String | Pathname | Array[String | Pathname])) -> void



245
246
247
248
249
250
251
252
253
254
255
# File 'lib/zeitwerk/loader/config.rb', line 245

def collapse(*glob_patterns)
  glob_patterns = expand_paths(glob_patterns)
  mutex.synchronize do
    collapse_glob_patterns.merge(glob_patterns)
    new_collapse_dirs = expand_glob_patterns(glob_patterns)
    collapse_dirs.merge(new_collapse_dirs)
    new_collapse_dirs.each do |dir|
      collapse_parents << File.dirname(dir)
    end
  end
end

#dirs(namespaces: false, ignored: false) ⇒ Object

If ‘namespaces` is falsey (default), returns an array with the absolute paths of the root directories as strings. If truthy, returns a hash table instead. Keys are the absolute paths of the root directories as strings, values are their corresponding namespaces, class or module objects.

If ‘ignored` is falsey (default), ignored root directories are filtered out.

These are read-only collections, please add to them with ‘push_dir`.

: (?namespaces: boolish, ?ignored: boolish) -> Array | Hash[String, Module]



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/zeitwerk/loader/config.rb', line 186

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

#do_not_eager_load(*paths) ⇒ Object

Let eager load ignore the given files or directories. The constants defined in those files are still autoloadable.

: (*(String | Pathname | Array[String | Pathname])) -> void



227
228
229
# File 'lib/zeitwerk/loader/config.rb', line 227

def do_not_eager_load(*paths)
  mutex.synchronize { eager_load_exclusions.merge(expand_paths(paths)) }
end

#enable_reloadingObject

You need to call this method before setup in order to be able to reload. There is no way to undo this, either you want to reload or you don’t.

: () -> void ! Zeitwerk::Error



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/zeitwerk/loader/config.rb', line 206

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

#ignore(*glob_patterns) ⇒ Object

Configure files, directories, or glob patterns to be totally ignored.

: (*(String | Pathname | Array[String | Pathname])) -> void



234
235
236
237
238
239
240
# File 'lib/zeitwerk/loader/config.rb', line 234

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

#initializeObject

: () -> void



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/zeitwerk/loader/config.rb', line 103

def initialize
  @inflector              = Zeitwerk::Inflector.new
  @logger                 = self.class.default_logger
  @tag                    = SecureRandom.hex(3)
  @initialized_at         = Time.now
  @roots                  = {}
  @nsfile                 = nil
  @ignored_glob_patterns  = Set.new
  @ignored_paths          = Set.new
  @collapse_glob_patterns = Set.new
  @collapse_dirs          = Set.new
  @collapse_parents       = Set.new
  @eager_load_exclusions  = Set.new
  @reloading_enabled      = false
  @on_setup_callbacks     = []
  @on_load_callbacks      = {}
  @on_unload_callbacks    = {}
end

#log!Object

Logs to ‘$stdout`, handy shortcut for debugging.

: () -> void



331
332
333
# File 'lib/zeitwerk/loader/config.rb', line 331

def log!
  @logger = ->(msg) { puts msg }
end

#on_load(cpath = UNDEFINED, &block) ⇒ Object

Configure a block to be invoked once a certain constant path is loaded. Supports multiple callbacks, and if there are many, they are executed in the order in which they were defined.

loader.on_load('SomeApiClient') do |klass, _abspath|
  klass.endpoint = 'https://api.dev'
end

Can also be configured for any constant loaded:

loader.on_load do |cpath, value, abspath|
  # ...
end

: (String) { (top, String) -> void } -> void ! TypeError | NameError | { (String, top, String) -> void } -> void



284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/zeitwerk/loader/config.rb', line 284

def on_load(cpath = UNDEFINED, &block)
  key = if cpath.equal?(UNDEFINED)
    :ANY
  elsif !cpath.is_a?(String)
    raise TypeError, 'on_load only accepts strings'
  else
    @cpv.validate!(cpath)
  end

  mutex.synchronize do
    (on_load_callbacks[key] ||= []) << block
  end
end

#on_setup(&block) ⇒ Object

Configure a block to be called after setup and on each reload. If setup was already done, the block runs immediately.

: () { () -> void } -> void



261
262
263
264
265
266
# File 'lib/zeitwerk/loader/config.rb', line 261

def on_setup(&block)
  mutex.synchronize do
    on_setup_callbacks << block
    block.call if @setup
  end
end

#on_unload(cpath = UNDEFINED, &block) ⇒ Object

Configure a block to be invoked right before a certain constant is removed. Supports multiple callbacks, and if there are many, they are executed in the order in which they were defined.

loader.on_unload('Country') do |klass, _abspath|
  klass.clear_cache
end

Can also be configured for any removed constant:

loader.on_unload do |cpath, value, abspath|
  # ...
end

: (String) { (top, String) -> void } -> void ! TypeError | NameError | { (String, top, String) -> void } -> void



314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/zeitwerk/loader/config.rb', line 314

def on_unload(cpath = UNDEFINED, &block)
  key = if cpath.equal?(UNDEFINED)
    :ANY
  elsif !cpath.is_a?(String)
    raise TypeError, 'on_unload only accepts strings'
  else
    @cpv.validate!(cpath)
  end

  mutex.synchronize do
    (on_unload_callbacks[key] ||= []) << block
  end
end

#push_dir(path, namespace: Object) ⇒ Object

Pushes ‘path` to the list of root directories.

Raises ‘Zeitwerk::Error` if `path` does not exist, or if another loader in the same process already manages that directory or one of its ascendants or descendants.

: (String | Pathname, namespace: Module) -> void ! Zeitwerk::Error



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/zeitwerk/loader/config.rb', line 129

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 @fs.dir?(abspath)
    raise_if_conflicting_root_dir(abspath)
    roots[abspath] = namespace
  else
    raise Zeitwerk::Error, "the root directory #{abspath} does not exist"
  end
end

#reloading_enabled?Boolean

: () -> bool

Returns:

  • (Boolean)


219
220
221
# File 'lib/zeitwerk/loader/config.rb', line 219

def reloading_enabled?
  @reloading_enabled
end

#tagObject

Returns the loader’s tag.

Implemented as a method instead of via attr_reader for symmetry with the writer below.

: () -> String



153
154
155
# File 'lib/zeitwerk/loader/config.rb', line 153

def tag
  @tag
end

#tag=(tag) ⇒ Object

Sets a tag for the loader, useful for logging.

: (to_s() -> String) -> void



160
161
162
# File 'lib/zeitwerk/loader/config.rb', line 160

def tag=(tag)
  @tag = tag.to_s
end