zeitwerk 2.4.1 → 2.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +47 -0
- data/lib/zeitwerk/kernel.rb +1 -0
- data/lib/zeitwerk/loader.rb +22 -0
- data/lib/zeitwerk/loader/callbacks.rb +24 -6
- data/lib/zeitwerk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f45a7c3caf48f06e10dd513a7b074da7ec059fa3299751d361514aadc83d995e
|
4
|
+
data.tar.gz: c8c29793e95245b47b1283891791d2c20365b8c694b3dcdfb7daab0b74c16bdb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6194d326b268c9333ed9d2ac1c62b65756fa9af844c5fb7ad8272ecec33aa439015506758bcd329e421508facb4153e04e45da0efb0a2a42001a6f2e0a0d3b6a
|
7
|
+
data.tar.gz: 656009f40e777f641ff1dc80f54b63559514439538c73965aa9226b6c3e33c6be71c07eb30adfe7f4cd4b3be72aa6e42918392ba27167fb9502b9aed037f36d3
|
data/README.md
CHANGED
@@ -25,6 +25,7 @@
|
|
25
25
|
- [Zeitwerk::Inflector](#zeitwerkinflector)
|
26
26
|
- [Zeitwerk::GemInflector](#zeitwerkgeminflector)
|
27
27
|
- [Custom inflector](#custom-inflector)
|
28
|
+
- [The on_load callback](#the-on_load-callback)
|
28
29
|
- [Logging](#logging)
|
29
30
|
- [Loader tag](#loader-tag)
|
30
31
|
- [Ignoring parts of the project](#ignoring-parts-of-the-project)
|
@@ -502,6 +503,52 @@ class MyGem::Inflector < Zeitwerk::GemInflector
|
|
502
503
|
end
|
503
504
|
```
|
504
505
|
|
506
|
+
<a id="markdown-the-on_load-callback" name="the-on_load-callback"></a>
|
507
|
+
### The on_load callback
|
508
|
+
|
509
|
+
The usual place to run something when a file is loaded is the file itself. However, sometimes you'd like to be called, and this is possible with the `on_load` callback.
|
510
|
+
|
511
|
+
For example, let's imagine this class belongs to a Rails application:
|
512
|
+
|
513
|
+
```ruby
|
514
|
+
class SomeApiClient
|
515
|
+
class << self
|
516
|
+
attr_accessor :endpoint
|
517
|
+
end
|
518
|
+
end
|
519
|
+
```
|
520
|
+
|
521
|
+
With `on_load`, it is easy to schedule code at boot time that initializes `endpoint` according to the configuration:
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
# config/environments/development.rb
|
525
|
+
loader.on_load("SomeApiClient") do
|
526
|
+
SomeApiClient.endpoint = "https://api.dev"
|
527
|
+
end
|
528
|
+
|
529
|
+
# config/environments/production.rb
|
530
|
+
loader.on_load("SomeApiClient") do
|
531
|
+
SomeApiClient.endpoint = "https://api.prod"
|
532
|
+
end
|
533
|
+
```
|
534
|
+
|
535
|
+
Uses cases:
|
536
|
+
|
537
|
+
* Doing something with an autoloadable class or module in a Rails application during initialization, in a way that plays well with reloading. As in the previous example.
|
538
|
+
* Delaying the execution of the block until the class is loaded for performance.
|
539
|
+
* Delaying the execution of the block until the class is loaded because it follows the adapter pattern and better not to load the class if the user does not need it.
|
540
|
+
* Etc.
|
541
|
+
|
542
|
+
However, let me stress that the easiest way to accomplish that is to write whatever you have to do in the actual target file. `on_load` use cases are edgy, use it only if appropriate.
|
543
|
+
|
544
|
+
`on_load` receives the name of the target class or module as a string. The given block is executed every time its corresponding file is loaded. That includes reloads.
|
545
|
+
|
546
|
+
Multiple callbacks on the same target are supported, and they run in order of definition.
|
547
|
+
|
548
|
+
The block is executed once the loader has loaded the target. In particular, if the target was already loaded when the callback is defined, the block won't run. But if you reload and load the target again, then it will. Normally, you'll want to define `on_load` callbacks before `setup`.
|
549
|
+
|
550
|
+
Defining a callback for a target not managed by the receiver is not an error, the block simply won't ever be executed.
|
551
|
+
|
505
552
|
<a id="markdown-logging" name="logging"></a>
|
506
553
|
### Logging
|
507
554
|
|
data/lib/zeitwerk/kernel.rb
CHANGED
data/lib/zeitwerk/loader.rb
CHANGED
@@ -129,6 +129,9 @@ module Zeitwerk
|
|
129
129
|
# @sig Set[String]
|
130
130
|
attr_reader :eager_load_exclusions
|
131
131
|
|
132
|
+
# User-oriented callbacks to be fired when a constant is loaded.
|
133
|
+
attr_reader :on_load_callbacks
|
134
|
+
|
132
135
|
# @private
|
133
136
|
# @sig Mutex
|
134
137
|
attr_reader :mutex
|
@@ -155,6 +158,7 @@ module Zeitwerk
|
|
155
158
|
@to_unload = {}
|
156
159
|
@lazy_subdirs = {}
|
157
160
|
@eager_load_exclusions = Set.new
|
161
|
+
@on_load_callbacks = {}
|
158
162
|
|
159
163
|
# TODO: find a better name for these mutexes.
|
160
164
|
@mutex = Mutex.new
|
@@ -262,6 +266,24 @@ module Zeitwerk
|
|
262
266
|
end
|
263
267
|
end
|
264
268
|
|
269
|
+
# Configure a block to be invoked once a certain constant path is loaded.
|
270
|
+
# Supports multiple callbacks, and if there are many, they are executed in
|
271
|
+
# the order in which they were defined.
|
272
|
+
#
|
273
|
+
# loader.on_load("SomeApiClient") do
|
274
|
+
# SomeApiClient.endpoint = "https://api.dev"
|
275
|
+
# end
|
276
|
+
#
|
277
|
+
# @raise [TypeError]
|
278
|
+
# @sig (String) { () -> void } -> void
|
279
|
+
def on_load(cpath, &block)
|
280
|
+
raise TypeError, "on_load only accepts strings" unless cpath.is_a?(String)
|
281
|
+
|
282
|
+
mutex.synchronize do
|
283
|
+
(on_load_callbacks[cpath] ||= []) << block
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
265
287
|
# Sets autoloads in the root namespace and preloads files, if any.
|
266
288
|
#
|
267
289
|
# @sig () -> void
|
@@ -6,15 +6,19 @@ module Zeitwerk::Loader::Callbacks
|
|
6
6
|
# @private
|
7
7
|
# @sig (String) -> void
|
8
8
|
def on_file_autoloaded(file)
|
9
|
-
cref
|
10
|
-
|
9
|
+
cref = autoloads.delete(file)
|
10
|
+
cpath = cpath(*cref)
|
11
|
+
|
12
|
+
to_unload[cpath] = [file, cref] if reloading_enabled?
|
11
13
|
Zeitwerk::Registry.unregister_autoload(file)
|
12
14
|
|
13
15
|
if logger && cdef?(*cref)
|
14
|
-
log("constant #{cpath
|
16
|
+
log("constant #{cpath} loaded from file #{file}")
|
15
17
|
elsif !cdef?(*cref)
|
16
|
-
raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath
|
18
|
+
raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath}, but didn't", cref.last)
|
17
19
|
end
|
20
|
+
|
21
|
+
run_on_load_callbacks(cpath)
|
18
22
|
end
|
19
23
|
|
20
24
|
# Invoked from our decorated Kernel#require when a managed directory is
|
@@ -37,9 +41,10 @@ module Zeitwerk::Loader::Callbacks
|
|
37
41
|
mutex2.synchronize do
|
38
42
|
if cref = autoloads.delete(dir)
|
39
43
|
autovivified_module = cref[0].const_set(cref[1], Module.new)
|
40
|
-
|
44
|
+
cpath = autovivified_module.name
|
45
|
+
log("module #{cpath} autovivified from directory #{dir}") if logger
|
41
46
|
|
42
|
-
to_unload[
|
47
|
+
to_unload[cpath] = [dir, cref] if reloading_enabled?
|
43
48
|
|
44
49
|
# We don't unregister `dir` in the registry because concurrent threads
|
45
50
|
# wouldn't find a loader associated to it in Kernel#require and would
|
@@ -48,6 +53,8 @@ module Zeitwerk::Loader::Callbacks
|
|
48
53
|
autoloaded_dirs << dir
|
49
54
|
|
50
55
|
on_namespace_loaded(autovivified_module)
|
56
|
+
|
57
|
+
run_on_load_callbacks(cpath)
|
51
58
|
end
|
52
59
|
end
|
53
60
|
end
|
@@ -65,4 +72,15 @@ module Zeitwerk::Loader::Callbacks
|
|
65
72
|
end
|
66
73
|
end
|
67
74
|
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @sig (String) -> void
|
79
|
+
def run_on_load_callbacks(cpath)
|
80
|
+
# Very common, do not even compute a hash code.
|
81
|
+
return if on_load_callbacks.empty?
|
82
|
+
|
83
|
+
callbacks = reloading_enabled? ? on_load_callbacks[cpath] : on_load_callbacks.delete(cpath)
|
84
|
+
callbacks.each(&:call) if callbacks
|
85
|
+
end
|
68
86
|
end
|
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: 2.4.
|
4
|
+
version: 2.4.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: 2020-
|
11
|
+
date: 2020-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|