zeitwerk 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +11 -1
- data/lib/zeitwerk.rb +1 -0
- data/lib/zeitwerk/explicit_namespace.rb +77 -0
- data/lib/zeitwerk/loader.rb +9 -27
- data/lib/zeitwerk/ordered_set.rb +21 -0
- data/lib/zeitwerk/registry.rb +10 -0
- data/lib/zeitwerk/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40e2919df324c67a85e00ac31349b96349b1a74a39e97e3f41c513258f108542
|
4
|
+
data.tar.gz: 17c8c31d6a89e4ead544d1bc35143f548331923106decb05be67ebf57daf1bce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 455ff00c99726bb3003cebfcd4d264a4288a84d3d6d5bc7e8005ed4f0e7d66eff2abf3670b474bbeb5ca0a5517e1e7e316df64ccc065775b60e9b1822e5664cc
|
7
|
+
data.tar.gz: 4971b4baec41fcffa1075248dbb714e456b2a0c37e193bd4ad47f81c658751dcf9de687ed023d4b0e00a67e57c4fa219ff7db6fe82437901774c6a0e59861dbf
|
data/README.md
CHANGED
@@ -60,7 +60,9 @@ Main interface for gems:
|
|
60
60
|
# lib/my_gem.rb (main file)
|
61
61
|
|
62
62
|
require "zeitwerk"
|
63
|
-
Zeitwerk::Loader.for_gem
|
63
|
+
loader = Zeitwerk::Loader.for_gem
|
64
|
+
loader.setup # ready!
|
65
|
+
# loader.eager_load, optionally
|
64
66
|
|
65
67
|
module MyGem
|
66
68
|
# ...
|
@@ -150,6 +152,8 @@ class Hotel < ApplicationRecord
|
|
150
152
|
end
|
151
153
|
```
|
152
154
|
|
155
|
+
An explicit namespace must be managed by one single loader. Loaders that reopen namespaces owned by other projects are responsible for loading their constants before setup.
|
156
|
+
|
153
157
|
### Nested root directories
|
154
158
|
|
155
159
|
Root directories should not be ideally nested, but Zeitwerk supports them because in Rails, for example, both `app/models` and `app/models/concerns` belong to the autoload paths.
|
@@ -172,6 +176,8 @@ Loaders are ready to load code right after calling `setup` on them:
|
|
172
176
|
loader.setup
|
173
177
|
```
|
174
178
|
|
179
|
+
This method is synchronized and idempotent.
|
180
|
+
|
175
181
|
Customization should generally be done before that call. In particular, in the generic interface you may set the root directories from which you want to load files:
|
176
182
|
|
177
183
|
```ruby
|
@@ -212,6 +218,8 @@ loader.eager_load
|
|
212
218
|
|
213
219
|
That skips ignored files and directories.
|
214
220
|
|
221
|
+
Eager loading is synchronized and idempotent.
|
222
|
+
|
215
223
|
If you want to eager load yourself and all dependencies using Zeitwerk, you can broadcast the `eager_load` call to all instances:
|
216
224
|
|
217
225
|
```ruby
|
@@ -220,6 +228,8 @@ Zeitwerk::Loader.eager_load_all
|
|
220
228
|
|
221
229
|
This may be handy in top-level services, like web applications.
|
222
230
|
|
231
|
+
Note that thanks to idempotence `Zeitwerk::Loader.eager_load_all` won't eager load twice if any of the instances already eager loaded.
|
232
|
+
|
223
233
|
### Preloading
|
224
234
|
|
225
235
|
Zeitwerk instances are able to preload files and directories.
|
data/lib/zeitwerk.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Zeitwerk
|
2
|
+
# Centralizes the logic for the trace point used to detect the creation of
|
3
|
+
# explicit namespaces, needed to descend into matching subdirectories right
|
4
|
+
# after the constant has been defined.
|
5
|
+
#
|
6
|
+
# The implementation assumes an explicit namespace is managed by one loader.
|
7
|
+
# Loaders that reopen namespaces owned by other projects are responsible for
|
8
|
+
# loading their constant before setup. This is documented.
|
9
|
+
module ExplicitNamespace # :nodoc: all
|
10
|
+
class << self
|
11
|
+
# Maps constant paths that correspond to explicit namespaces according to
|
12
|
+
# the file system, to the loader responsible for them.
|
13
|
+
#
|
14
|
+
# @private
|
15
|
+
# @return [{String => Zeitwerk::Loader}]
|
16
|
+
attr_reader :cpaths
|
17
|
+
|
18
|
+
# @private
|
19
|
+
# @return [Mutex]
|
20
|
+
attr_reader :mutex
|
21
|
+
|
22
|
+
# @private
|
23
|
+
# @return [TracePoint]
|
24
|
+
attr_reader :tracer
|
25
|
+
|
26
|
+
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
|
27
|
+
# is responsible.
|
28
|
+
#
|
29
|
+
# @private
|
30
|
+
# @param cpath [String]
|
31
|
+
# @param loader [Zeitwerk::Loader]
|
32
|
+
# @return [void]
|
33
|
+
def register(cpath, loader)
|
34
|
+
mutex.synchronize do
|
35
|
+
cpaths[cpath] = loader
|
36
|
+
# We check enabled? because, looking at the C source code, enabling an
|
37
|
+
# enabled tracer does not seem to be a simple no-op.
|
38
|
+
tracer.enable unless tracer.enabled?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private
|
43
|
+
# @param loader [Zeitwerk::Loader]
|
44
|
+
# @return [void]
|
45
|
+
def unregister(loader)
|
46
|
+
cpaths.delete_if { |_cpath, l| l == loader }
|
47
|
+
disable_tracer_if_unneeded
|
48
|
+
end
|
49
|
+
|
50
|
+
# Utility for the test suite
|
51
|
+
#
|
52
|
+
# @private
|
53
|
+
# @return [void]
|
54
|
+
def teardown
|
55
|
+
cpaths.clear
|
56
|
+
tracer.disable
|
57
|
+
end
|
58
|
+
|
59
|
+
def disable_tracer_if_unneeded
|
60
|
+
mutex.synchronize do
|
61
|
+
tracer.disable if cpaths.empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@cpaths = {}
|
67
|
+
@mutex = Mutex.new
|
68
|
+
@tracer = TracePoint.new(:class) do |event|
|
69
|
+
# Note that it makes sense to compute the hash code unconditionally,
|
70
|
+
# because if cpaths was empty the trace point would be disabled.
|
71
|
+
if loader = cpaths.delete(event.self.name)
|
72
|
+
loader.on_namespace_loaded(event.self)
|
73
|
+
disable_tracer_if_unneeded
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/zeitwerk/loader.rb
CHANGED
@@ -92,14 +92,6 @@ module Zeitwerk
|
|
92
92
|
# @return [Mutex]
|
93
93
|
attr_reader :mutex
|
94
94
|
|
95
|
-
# This tracer listens to `:class` events, and it is used to support explicit
|
96
|
-
# namespaces. Benchmarks have shown the tracer does not impact performance
|
97
|
-
# in any measurable way.
|
98
|
-
#
|
99
|
-
# @private
|
100
|
-
# @return [TracePoint]
|
101
|
-
attr_reader :tracer
|
102
|
-
|
103
95
|
def initialize
|
104
96
|
@initialized_at = Time.now
|
105
97
|
|
@@ -119,10 +111,6 @@ module Zeitwerk
|
|
119
111
|
@setup = false
|
120
112
|
@eager_loaded = false
|
121
113
|
|
122
|
-
@tracer = TracePoint.new(:class) do |tp|
|
123
|
-
on_namespace_loaded(tp.self)
|
124
|
-
end
|
125
|
-
|
126
114
|
Registry.register_loader(self)
|
127
115
|
end
|
128
116
|
|
@@ -230,8 +218,8 @@ module Zeitwerk
|
|
230
218
|
lazy_subdirs.clear
|
231
219
|
|
232
220
|
Registry.on_unload(self)
|
221
|
+
ExplicitNamespace.unregister(self)
|
233
222
|
|
234
|
-
disable_tracer
|
235
223
|
@setup = false
|
236
224
|
end
|
237
225
|
end
|
@@ -268,7 +256,6 @@ module Zeitwerk
|
|
268
256
|
end
|
269
257
|
end
|
270
258
|
|
271
|
-
disable_tracer
|
272
259
|
@eager_loaded = true
|
273
260
|
end
|
274
261
|
end
|
@@ -359,11 +346,12 @@ module Zeitwerk
|
|
359
346
|
# @return [void]
|
360
347
|
def autoload_subdir(parent, cname, subdir)
|
361
348
|
if autoload_path = autoload_for?(parent, cname)
|
362
|
-
|
349
|
+
cpath = cpath(parent, cname)
|
350
|
+
register_explicit_namespace(cpath) if ruby?(autoload_path)
|
363
351
|
# We do not need to issue another autoload, the existing one is enough
|
364
352
|
# no matter if it is for a file or a directory. Just remember the
|
365
353
|
# subdirectory has to be visited if the namespace is used.
|
366
|
-
(lazy_subdirs[cpath
|
354
|
+
(lazy_subdirs[cpath] ||= []) << subdir
|
367
355
|
elsif !cdef?(parent, cname)
|
368
356
|
# First time we find this namespace, set an autoload for it.
|
369
357
|
(lazy_subdirs[cpath(parent, cname)] ||= []) << subdir
|
@@ -390,7 +378,7 @@ module Zeitwerk
|
|
390
378
|
Registry.unregister_autoload(autoload_path)
|
391
379
|
|
392
380
|
set_autoload(parent, cname, file)
|
393
|
-
|
381
|
+
register_explicit_namespace(cpath(parent, cname))
|
394
382
|
elsif !cdef?(parent, cname)
|
395
383
|
set_autoload(parent, cname, file)
|
396
384
|
end
|
@@ -508,20 +496,14 @@ module Zeitwerk
|
|
508
496
|
logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
|
509
497
|
end
|
510
498
|
|
511
|
-
def enable_tracer
|
512
|
-
# We check enabled? because, looking at the C source code, enabling an
|
513
|
-
# enabled tracer does not seem to be a simple no-op.
|
514
|
-
tracer.enable if !tracer.enabled?
|
515
|
-
end
|
516
|
-
|
517
|
-
def disable_tracer
|
518
|
-
tracer.disable if tracer.enabled?
|
519
|
-
end
|
520
|
-
|
521
499
|
def cdef?(parent, cname)
|
522
500
|
parent.const_defined?(cname, false)
|
523
501
|
end
|
524
502
|
|
503
|
+
def register_explicit_namespace(cpath)
|
504
|
+
ExplicitNamespace.register(cpath, self)
|
505
|
+
end
|
506
|
+
|
525
507
|
def raise_if_conflicting_directory(dir)
|
526
508
|
self.class.mutex.synchronize do
|
527
509
|
Registry.loaders.each do |loader|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Zeitwerk
|
2
|
+
class OrderedSet
|
3
|
+
def initialize
|
4
|
+
@set = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(object)
|
8
|
+
@set[object] = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def del(object)
|
12
|
+
@set.delete(object)
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
@set.each_key do |key|
|
17
|
+
yield key
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/zeitwerk/registry.rb
CHANGED
@@ -137,6 +137,16 @@ module Zeitwerk
|
|
137
137
|
autoloads.delete_if { |_path, object| object == loader }
|
138
138
|
inceptions.delete_if { |_cpath, (_path, object)| object == loader }
|
139
139
|
end
|
140
|
+
|
141
|
+
# Utility for the test suite
|
142
|
+
#
|
143
|
+
# @private
|
144
|
+
# @return [void]
|
145
|
+
def teardown
|
146
|
+
loaders.each(&:unload)
|
147
|
+
loaders.clear
|
148
|
+
loaders_managing_gems.clear
|
149
|
+
end
|
140
150
|
end
|
141
151
|
|
142
152
|
@loaders = []
|
data/lib/zeitwerk/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeitwerk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Xavier Noria
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -23,11 +23,13 @@ files:
|
|
23
23
|
- README.md
|
24
24
|
- lib/zeitwerk.rb
|
25
25
|
- lib/zeitwerk/conflicting_directory.rb
|
26
|
+
- lib/zeitwerk/explicit_namespace.rb
|
26
27
|
- lib/zeitwerk/gem_inflector.rb
|
27
28
|
- lib/zeitwerk/inflector.rb
|
28
29
|
- lib/zeitwerk/kernel.rb
|
29
30
|
- lib/zeitwerk/loader.rb
|
30
31
|
- lib/zeitwerk/loader/callbacks.rb
|
32
|
+
- lib/zeitwerk/ordered_set.rb
|
31
33
|
- lib/zeitwerk/registry.rb
|
32
34
|
- lib/zeitwerk/version.rb
|
33
35
|
homepage: https://github.com/fxn/zeitwerk
|