/
kernel.rb
68 lines (64 loc) · 2.69 KB
/
kernel.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
# frozen_string_literal: true
module Kernel
module_function
# Zeitwerk's main idea is to define autoloads for project constants, and then
# intercept them when triggered in this thin `Kernel#require` wrapper.
#
# That allows us to complete the circle, invoke callbacks, autovivify modules,
# define autoloads for just autoloaded namespaces, update internal state, etc.
#
# On the other hand, if you publish a new version of a gem that is now managed
# by Zeitwerk, client code can reference directly your classes and modules and
# should not require anything. But if someone has legacy require calls around,
# they will work as expected, and in a compatible way. This feature is by now
# EXPERIMENTAL and UNDOCUMENTED.
#
# We cannot decorate with prepend + super because Kernel has already been
# included in Object, and changes in ancestors don't get propagated into
# already existing ancestor chains on Ruby < 3.0.
alias_method :zeitwerk_original_require, :require
class << self
alias_method :zeitwerk_original_require, :require
end
# @sig (String) -> true | false
def require(path)
if loader = Zeitwerk::Registry.loader_for(path)
if path.end_with?(".rb")
required = zeitwerk_original_require(path)
loader.on_file_autoloaded(path) if required
required
else
loader.on_dir_autoloaded(path)
true
end
else
required = zeitwerk_original_require(path)
if required
abspath = $LOADED_FEATURES.last
if loader = Zeitwerk::Registry.loader_for(abspath)
loader.on_file_autoloaded(abspath)
end
end
required
end
end
# By now, I have seen no way so far to decorate require_relative.
#
# For starters, at least in CRuby, require_relative does not delegate to
# require. Both require and require_relative delegate the bulk of their work
# to an internal C function called rb_require_safe. So, our require wrapper is
# not executed.
#
# On the other hand, we cannot use the aliasing technique above because
# require_relative receives a path relative to the directory of the file in
# which the call is performed. If a wrapper here invoked the original method,
# Ruby would resolve the relative path taking lib/zeitwerk as base directory.
#
# A workaround could be to extract the base directory from caller_locations,
# but what if someone else decorated require_relative before us? You can't
# really know with certainty where's the original call site in the stack.
#
# However, the main use case for require_relative is to load files from your
# own project. Projects managed by Zeitwerk don't do this for files managed by
# Zeitwerk, precisely.
end