zeitwerk 2.1.6 → 2.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -10
- data/lib/zeitwerk/gem_inflector.rb +1 -1
- data/lib/zeitwerk/inflector.rb +1 -1
- data/lib/zeitwerk/loader.rb +11 -28
- data/lib/zeitwerk/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a89545605d519214e873572adc81662ece65e5d3aa6ffaa19f04877beb2d576
|
4
|
+
data.tar.gz: ac086c60c9391fbdaed5875b88038e6a977bf63110e39c38357b160056997b07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58326a65a566e5c72e8e2a588eb2dd4dda97efc485ebe422dcc63d9e9b3e83395f9dac5a3e35fe0cb9b17efc75cca3a41f73e3d3ac99b9b49a9d999d9e73b551
|
7
|
+
data.tar.gz: 24475fe5d47dadf250ce4a658bc44ecd72d29f6e0cd87ce8502d62eaa2e4ba7dbecb2bea2a28cb3279a3fb19bb5aa82b5676b7151b9f301e3bea607b2d75854c
|
data/README.md
CHANGED
@@ -29,6 +29,7 @@
|
|
29
29
|
- [Use case: The adapter pattern](#use-case-the-adapter-pattern)
|
30
30
|
- [Use case: Test files mixed with implementation files](#use-case-test-files-mixed-with-implementation-files)
|
31
31
|
- [Edge cases](#edge-cases)
|
32
|
+
- [Rules of thumb](#rules-of-thumb)
|
32
33
|
- [Pronunciation](#pronunciation)
|
33
34
|
- [Supported Ruby versions](#supported-ruby-versions)
|
34
35
|
- [Motivation](#motivation)
|
@@ -42,15 +43,13 @@
|
|
42
43
|
|
43
44
|
Zeitwerk is an efficient and thread-safe code loader for Ruby.
|
44
45
|
|
45
|
-
Given a conventional file structure, Zeitwerk
|
46
|
+
Given a [conventional file structure](#file-structure), Zeitwerk is able to load your project's classes and modules on demand (autoloading), or upfront (eager loading). You don't need to write `require` calls for your own files, rather, you can streamline your programming knowing that your classes and modules are available everywhere. This feature is efficient, thread-safe, and matches Ruby's semantics for constants.
|
46
47
|
|
47
|
-
Zeitwerk
|
48
|
+
Zeitwerk is also able to reload code, which may be handy while developing web applications. Coordination is needed to reload in a thread-safe manner. The documentation below explains how to do this.
|
48
49
|
|
49
|
-
The
|
50
|
+
The gem is designed so that any project, gem dependency, application, etc. can have their own independent loader, coexisting in the same process, managing their own project trees, and independent of each other. Each loader has its own configuration, inflector, and optional logger.
|
50
51
|
|
51
|
-
Zeitwerk
|
52
|
-
|
53
|
-
Finally, in some production setups it may be optimal to eager load all code upfront. Zeitwerk is able to do that too.
|
52
|
+
Internally, Zeitwerk issues `require` calls exclusively using absolute file names, so there are no costly file system lookups in `$LOAD_PATH`. Technically, the directories managed by Zeitwerk do not even need to be in `$LOAD_PATH`. Furthermore, Zeitwerk does only one single scan of the project tree, and it descends into subdirectories lazily, only if their namespaces are used.
|
54
53
|
|
55
54
|
<a id="markdown-synopsis" name="synopsis"></a>
|
56
55
|
## Synopsis
|
@@ -62,8 +61,8 @@ Main interface for gems:
|
|
62
61
|
|
63
62
|
require "zeitwerk"
|
64
63
|
loader = Zeitwerk::Loader.for_gem
|
65
|
-
loader.setup
|
66
|
-
|
64
|
+
loader.setup # ready!
|
65
|
+
loader.eager_load # optionally
|
67
66
|
|
68
67
|
module MyGem
|
69
68
|
# ...
|
@@ -205,7 +204,7 @@ Zeitwerk works internally only with absolute paths to avoid costly file searches
|
|
205
204
|
<a id="markdown-reloading" name="reloading"></a>
|
206
205
|
### Reloading
|
207
206
|
|
208
|
-
|
207
|
+
Zeitwerk is able to reload code, but you need to enable this feature:
|
209
208
|
|
210
209
|
```ruby
|
211
210
|
loader = Zeitwerk::Loader.new
|
@@ -340,7 +339,7 @@ This needs to be done before calling `setup`.
|
|
340
339
|
|
341
340
|
Zeitwerk is silent by default, but you can ask loaders to trace their activity. Logging is meant just for troubleshooting, shouldn't normally be enabled.
|
342
341
|
|
343
|
-
The `log!`
|
342
|
+
The `log!` method is a quick shortcut to let the loader log to `$stdout`:
|
344
343
|
|
345
344
|
```
|
346
345
|
loader.log!
|
@@ -501,6 +500,21 @@ Trip = Struct.new { ... } # NOT SUPPORTED
|
|
501
500
|
|
502
501
|
This only affects explicit namespaces, those idioms work well for any other ordinary class or module.
|
503
502
|
|
503
|
+
<a id="markdown-rules-of-thumb" name="rules-of-thumb"></a>
|
504
|
+
### Rules of thumb
|
505
|
+
|
506
|
+
1. Different loaders should manage different directory trees. It is an error condition to configure overlapping root directories in different loaders.
|
507
|
+
|
508
|
+
2. Think the mere existence of a file is effectively like writing a `require` call for them, which is executed on demand (autoload) or upfront (eager load).
|
509
|
+
|
510
|
+
3. In that line, if two loaders manage files that translate to the same constant in the same namespace, the first one wins, the rest are ignored. Similar to what happens with `require` and `$LOAD_PATH`, only the first occurrence matters.
|
511
|
+
|
512
|
+
4. Projects that reopen a namespace defined by some dependency have to ensure said namespace is loaded before setup. That is, the project has to make sure it reopens, rather than define. This is often accomplished just loading the dependency.
|
513
|
+
|
514
|
+
5. Objects stored in reloadable constants should not be cached in places that are not reloaded. For example, non-reloadable classes should not subclass a reloadable class, or mixin a reloadable module. Otherwise, after reloading, those classes or module objects would become stale. Referring to constants in dynamic places like method calls or lambdas is fine.
|
515
|
+
|
516
|
+
6. In a given process, ideally, there should be at most one loader with reloading enabled. Technically, you can have more, but it may get tricky if one refers to constants managed by the other one. Do that only if you know what you are doing.
|
517
|
+
|
504
518
|
<a id="markdown-pronunciation" name="pronunciation"></a>
|
505
519
|
## Pronunciation
|
506
520
|
|
data/lib/zeitwerk/inflector.rb
CHANGED
data/lib/zeitwerk/loader.rb
CHANGED
@@ -53,23 +53,6 @@ module Zeitwerk
|
|
53
53
|
# @return [Set<String>]
|
54
54
|
attr_reader :ignored_paths
|
55
55
|
|
56
|
-
# A _shadowed file_ is a file managed by this loader that is ignored when
|
57
|
-
# setting autoloads because its matching constant is taken. Either the
|
58
|
-
# constant is already defined, or there exists an autoload for it.
|
59
|
-
#
|
60
|
-
# Think $LOAD_PATH and require, only the first occurrence of a given
|
61
|
-
# relative name is loaded.
|
62
|
-
#
|
63
|
-
# This set keeps track of the absolute path of shadowed files, to be able to
|
64
|
-
# skip them while eager loading.
|
65
|
-
#
|
66
|
-
# Note this cannot be implemented with do_not_eager_load because these
|
67
|
-
# files are not autoloadable.
|
68
|
-
#
|
69
|
-
# @private
|
70
|
-
# @return [Set<String>]
|
71
|
-
attr_reader :shadowed_files
|
72
|
-
|
73
56
|
# Maps real absolute paths for which an autoload has been set ---and not
|
74
57
|
# executed--- to their corresponding parent class or module and constant
|
75
58
|
# name.
|
@@ -155,7 +138,6 @@ module Zeitwerk
|
|
155
138
|
@autoloaded_dirs = []
|
156
139
|
@to_unload = {}
|
157
140
|
@lazy_subdirs = {}
|
158
|
-
@shadowed_files = Set.new
|
159
141
|
@eager_load_exclusions = Set.new
|
160
142
|
|
161
143
|
# TODO: find a better name for these mutexes.
|
@@ -324,7 +306,6 @@ module Zeitwerk
|
|
324
306
|
autoloaded_dirs.clear
|
325
307
|
to_unload.clear
|
326
308
|
lazy_subdirs.clear
|
327
|
-
shadowed_files.clear
|
328
309
|
|
329
310
|
Registry.on_unload(self)
|
330
311
|
ExplicitNamespace.unregister(self)
|
@@ -361,20 +342,24 @@ module Zeitwerk
|
|
361
342
|
break if @eager_loaded
|
362
343
|
|
363
344
|
queue = actual_root_dirs.reject { |dir| eager_load_exclusions.member?(dir) }
|
364
|
-
|
365
|
-
|
345
|
+
queue.map! { |dir| [Object, dir] }
|
346
|
+
while to_eager_load = queue.shift
|
347
|
+
namespace, dir = to_eager_load
|
348
|
+
|
349
|
+
ls(dir) do |basename, abspath|
|
366
350
|
next if eager_load_exclusions.member?(abspath)
|
367
351
|
|
368
352
|
if ruby?(abspath)
|
369
|
-
|
370
|
-
|
371
|
-
|
353
|
+
if cref = autoloads[File.realpath(abspath)]
|
354
|
+
cref[0].const_get(cref[1], false)
|
355
|
+
end
|
356
|
+
elsif dir?(abspath) && !root_dirs.key?(abspath)
|
357
|
+
cname = inflector.camelize(basename, abspath)
|
358
|
+
queue << [namespace.const_get(cname, false), abspath]
|
372
359
|
end
|
373
360
|
end
|
374
361
|
end
|
375
362
|
|
376
|
-
shadowed_files.clear
|
377
|
-
|
378
363
|
autoloaded_dirs.each do |autoloaded_dir|
|
379
364
|
Registry.unregister_autoload(autoloaded_dir)
|
380
365
|
end
|
@@ -526,7 +511,6 @@ module Zeitwerk
|
|
526
511
|
if autoload_path = autoload_for?(parent, cname)
|
527
512
|
# First autoload for a Ruby file wins, just ignore subsequent ones.
|
528
513
|
if ruby?(autoload_path)
|
529
|
-
shadowed_files.add(file)
|
530
514
|
log("file #{file} is ignored because #{autoload_path} has precedence") if logger
|
531
515
|
else
|
532
516
|
promote_namespace_from_implicit_to_explicit(
|
@@ -537,7 +521,6 @@ module Zeitwerk
|
|
537
521
|
)
|
538
522
|
end
|
539
523
|
elsif cdef?(parent, cname)
|
540
|
-
shadowed_files.add(file)
|
541
524
|
log("file #{file} is ignored because #{cpath(parent, cname)} is already defined") if logger
|
542
525
|
else
|
543
526
|
set_autoload(parent, cname, file)
|
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.1.
|
4
|
+
version: 2.1.8
|
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-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -50,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
50
|
- !ruby/object:Gem::Version
|
51
51
|
version: '0'
|
52
52
|
requirements: []
|
53
|
-
rubygems_version: 3.0.
|
53
|
+
rubygems_version: 3.0.3
|
54
54
|
signing_key:
|
55
55
|
specification_version: 4
|
56
56
|
summary: Efficient and thread-safe constant autoloader
|