-
-
Notifications
You must be signed in to change notification settings - Fork 119
/
explicit_namespace.rb
72 lines (65 loc) · 2.31 KB
/
explicit_namespace.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
69
70
71
72
module Zeitwerk
# Centralizes the logic for the trace point used to detect the creation of
# explicit namespaces, needed to descend into matching subdirectories right
# after the constant has been defined.
#
# The implementation assumes an explicit namespace is managed by one loader.
# Loaders that reopen namespaces owned by other projects are responsible for
# loading their constant before setup. This is documented.
module ExplicitNamespace # :nodoc: all
class << self
# Maps constant paths that correspond to explicit namespaces according to
# the file system, to the loader responsible for them.
#
# @private
# @return [{String => Zeitwerk::Loader}]
attr_reader :cpaths
# @private
# @return [Mutex]
attr_reader :mutex
# @private
# @return [TracePoint]
attr_reader :tracer
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
# is responsible.
#
# @private
# @param cpath [String]
# @param loader [Zeitwerk::Loader]
# @return [void]
def register(cpath, loader)
mutex.synchronize do
cpaths[cpath] = loader
# We check enabled? because, looking at the C source code, enabling an
# enabled tracer does not seem to be a simple no-op.
tracer.enable unless tracer.enabled?
end
end
# @private
# @param loader [Zeitwerk::Loader]
# @return [void]
def unregister(loader)
cpaths.delete_if { |_cpath, l| l == loader }
disable_tracer_if_unneeded
end
def disable_tracer_if_unneeded
mutex.synchronize do
tracer.disable if cpaths.empty?
end
end
end
@cpaths = {}
@mutex = Mutex.new
@tracer = TracePoint.new(:class) do |event|
# If the class is a singleton class, we won't do anything with it so we can bail out immediately.
# This is several orders of magnitude faster than accessing `Module#name`.
next if event.self.singleton_class?
# Note that it makes sense to compute the hash code unconditionally,
# because the trace point is disabled if cpaths is empty.
if loader = cpaths.delete(event.self.name)
loader.on_namespace_loaded(event.self)
disable_tracer_if_unneeded
end
end
end
end