Module: Zeitwerk::Loader::EagerLoad

Included in:
Zeitwerk::Loader
Defined in:
lib/zeitwerk/loader/eager_load.rb

Instance Method Summary collapse

Instance Method Details

#eager_load(force: false) ⇒ Object

Eager loads all files in the root directories, recursively. Files do not need to be in ‘$LOAD_PATH`, absolute file names are used. Ignored and shadowed files are not eager loaded. You can opt-out specifically in specific files and directories with `do_not_eager_load`, and that can be overridden passing `force: true`.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/zeitwerk/loader/eager_load.rb', line 9

def eager_load(force: false)
  mutex.synchronize do
    break if @eager_loaded
    raise Zeitwerk::SetupRequired unless @setup

    log("eager load start") if logger

    actual_roots.each do |root_dir, root_namespace|
      actual_eager_load_dir(root_dir, root_namespace, force: force)
    end

    autoloaded_dirs.each do |autoloaded_dir|
      Zeitwerk::Registry.unregister_autoload(autoloaded_dir)
    end
    autoloaded_dirs.clear

    @eager_loaded = true

    log("eager load end") if logger
  end
end

#eager_load_dir(path) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/zeitwerk/loader/eager_load.rb', line 32

def eager_load_dir(path)
  raise Zeitwerk::SetupRequired unless @setup

  abspath = File.expand_path(path)

  raise Zeitwerk::Error.new("#{abspath} is not a directory") unless dir?(abspath)

  cnames = []

  root_namespace = nil
  walk_up(abspath) do |dir|
    return if ignored_path?(dir)
    return if eager_load_exclusions.member?(dir)

    break if root_namespace = roots[dir]

    basename = File.basename(dir)
    return if hidden?(basename)

    unless collapse?(dir)
      cnames << inflector.camelize(basename, dir).to_sym
    end
  end

  raise Zeitwerk::Error.new("I do not manage #{abspath}") unless root_namespace

  return if @eager_loaded

  namespace = root_namespace
  cnames.reverse_each do |cname|
    # Can happen if there are no Ruby files. This is not an error condition,
    # the directory is actually managed. Could have Ruby files later.
    return unless cdef?(namespace, cname)
    namespace = cget(namespace, cname)
  end

  # A shortcircuiting test depends on the invocation of this method. Please
  # keep them in sync if refactored.
  actual_eager_load_dir(abspath, namespace)
end

#eager_load_namespace(mod) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/zeitwerk/loader/eager_load.rb', line 74

def eager_load_namespace(mod)
  raise Zeitwerk::SetupRequired unless @setup

  unless mod.is_a?(Module)
    raise Zeitwerk::Error, "#{mod.inspect} is not a class or module object"
  end

  return if @eager_loaded

  mod_name = real_mod_name(mod)
  return unless mod_name

  actual_roots.each do |root_dir, root_namespace|
    if mod.equal?(Object)
      # A shortcircuiting test depends on the invocation of this method.
      # Please keep them in sync if refactored.
      actual_eager_load_dir(root_dir, root_namespace)
    elsif root_namespace.equal?(Object)
      eager_load_child_namespace(mod, mod_name, root_dir, root_namespace)
    else
      root_namespace_name = real_mod_name(root_namespace)
      if root_namespace_name.start_with?(mod_name + "::")
        actual_eager_load_dir(root_dir, root_namespace)
      elsif mod_name == root_namespace_name
        actual_eager_load_dir(root_dir, root_namespace)
      elsif mod_name.start_with?(root_namespace_name + "::")
        eager_load_child_namespace(mod, mod_name, root_dir, root_namespace)
      else
        # Unrelated constant hierarchies, do nothing.
      end
    end
  end
end

#load_file(path) ⇒ Object

Loads the given Ruby file.

Raises if the argument is ignored, shadowed, or not managed by the receiver.

The method is implemented as ‘constantize` for files, in a sense, to be able to descend orderly and make sure the file is loadable.

Raises:



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/zeitwerk/loader/eager_load.rb', line 116

def load_file(path)
  abspath = File.expand_path(path)

  raise Zeitwerk::Error.new("#{abspath} does not exist") unless File.exist?(abspath)
  raise Zeitwerk::Error.new("#{abspath} is not a Ruby file") if dir?(abspath) || !ruby?(abspath)
  raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(abspath)

  basename = File.basename(abspath, ".rb")
  raise Zeitwerk::Error.new("#{abspath} is ignored") if hidden?(basename)

  base_cname = inflector.camelize(basename, abspath).to_sym

  root_namespace = nil
  cnames = []

  walk_up(File.dirname(abspath)) do |dir|
    raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(dir)

    break if root_namespace = roots[dir]

    basename = File.basename(dir)
    raise Zeitwerk::Error.new("#{abspath} is ignored") if hidden?(basename)

    unless collapse?(dir)
      cnames << inflector.camelize(basename, dir).to_sym
    end
  end

  raise Zeitwerk::Error.new("I do not manage #{abspath}") unless root_namespace

  namespace = root_namespace
  cnames.reverse_each do |cname|
    namespace = cget(namespace, cname)
  end

  raise Zeitwerk::Error.new("#{abspath} is shadowed") if shadowed_file?(abspath)

  cget(namespace, base_cname)
end