zeitwerk 1.1.0 → 1.2.0.beta
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 +26 -7
- data/lib/zeitwerk.rb +1 -0
- data/lib/zeitwerk/conflicting_directory.rb +2 -0
- data/lib/zeitwerk/loader.rb +47 -28
- data/lib/zeitwerk/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08f2b427ddbb6d8f7b995820ebafc52933ae2682fd19779097b6ab8c95205644'
|
4
|
+
data.tar.gz: 16b8e508f9026e87f2a66ecfe587a4b19dcc213e3393743b00d82e560ea601fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a41b015721362e40fdc5ae4402e388dfd38814d642a87e89aa4a90f36dca863cb562be68b11269817269a58862bf89761f1b671ff9be8b24dd220114a85298e
|
7
|
+
data.tar.gz: b2f7d5b6f61ee132a0608550f1e18bc58c8b2eff7eba154534742945373c4409e82eb7556a09a4179aca903d9b0c2136d2833902e2a3ea1743f4ade792902c91
|
data/README.md
CHANGED
@@ -24,9 +24,9 @@
|
|
24
24
|
- [Logging](#logging)
|
25
25
|
- [Loader tag](#loader-tag)
|
26
26
|
- [Ignoring parts of the project](#ignoring-parts-of-the-project)
|
27
|
-
- [Files that do not follow the conventions](#files-that-do-not-follow-the-conventions)
|
28
|
-
- [The adapter pattern](#the-adapter-pattern)
|
29
|
-
- [Test files mixed with implementation files](#test-files-mixed-with-implementation-files)
|
27
|
+
- [Use case: Files that do not follow the conventions](#use-case-files-that-do-not-follow-the-conventions)
|
28
|
+
- [Use case: The adapter pattern](#use-case-the-adapter-pattern)
|
29
|
+
- [Use case: Test files mixed with implementation files](#use-case-test-files-mixed-with-implementation-files)
|
30
30
|
- [Edge cases](#edge-cases)
|
31
31
|
- [Pronunciation](#pronunciation)
|
32
32
|
- [Supported Ruby versions](#supported-ruby-versions)
|
@@ -336,7 +336,7 @@ You can ignore file names, directory names, and glob patterns. Glob patterns are
|
|
336
336
|
|
337
337
|
Let's see some use cases.
|
338
338
|
|
339
|
-
#### Files that do not follow the conventions
|
339
|
+
#### Use case: Files that do not follow the conventions
|
340
340
|
|
341
341
|
Let's suppose that your gem decorates something in `Kernel`:
|
342
342
|
|
@@ -356,9 +356,28 @@ loader.ignore(kernel_ext)
|
|
356
356
|
loader.setup
|
357
357
|
```
|
358
358
|
|
359
|
-
|
359
|
+
You can also ignore the whole directory:
|
360
360
|
|
361
|
-
|
361
|
+
```ruby
|
362
|
+
core_ext = "#{__dir__}/my_gem/core_ext"
|
363
|
+
loader.ignore(core_ext)
|
364
|
+
loader.setup
|
365
|
+
```
|
366
|
+
|
367
|
+
#### Use case: The adapter pattern
|
368
|
+
|
369
|
+
Another use case for ignoring files is the adapter pattern.
|
370
|
+
|
371
|
+
Let's imagine your project talks to databases, supports several, and has adapters for each one of them. Those adapters may have top-level `require` calls that load their respective drivers:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
# my_gem/db_adapters/postgresql.rb
|
375
|
+
require "pg"
|
376
|
+
```
|
377
|
+
|
378
|
+
but you don't want your users to install them all, only the one they are going to use.
|
379
|
+
|
380
|
+
On the other hand, if your code is eager loaded by you or a parent project (with `Zeitwerk::Loader.eager_load_all`), those `require` calls are going to be executed. Ignoring the adapters prevents that:
|
362
381
|
|
363
382
|
```ruby
|
364
383
|
db_adapters = "#{__dir__}/my_gem/db_adapters"
|
@@ -372,7 +391,7 @@ The chosen adapter, then, has to be loaded by hand somehow:
|
|
372
391
|
require "my_gem/db_adapters/#{config[:db_adapter]}"
|
373
392
|
```
|
374
393
|
|
375
|
-
#### Test files mixed with implementation files
|
394
|
+
#### Use case: Test files mixed with implementation files
|
376
395
|
|
377
396
|
There are project layouts that put implementation files and test files together. To ignore the test files, you can use a glob pattern like this:
|
378
397
|
|
data/lib/zeitwerk.rb
CHANGED
data/lib/zeitwerk/loader.rb
CHANGED
@@ -145,9 +145,10 @@ module Zeitwerk
|
|
145
145
|
# @param path [<String, Pathname>]
|
146
146
|
# @return [void]
|
147
147
|
def push_dir(path)
|
148
|
-
|
149
|
-
|
148
|
+
self.class.mutex.synchronize do
|
149
|
+
abspath = File.expand_path(path)
|
150
150
|
if dir?(abspath)
|
151
|
+
raise_if_conflicting_directory(abspath)
|
151
152
|
root_dirs[abspath] = true
|
152
153
|
else
|
153
154
|
raise ArgumentError, "the root directory #{abspath} does not exist"
|
@@ -191,7 +192,7 @@ module Zeitwerk
|
|
191
192
|
mutex.synchronize do
|
192
193
|
unless @setup
|
193
194
|
expand_ignored_glob_patterns
|
194
|
-
non_ignored_root_dirs.each { |
|
195
|
+
non_ignored_root_dirs.each { |root_dir| set_autoloads_in_dir(root_dir, Object) }
|
195
196
|
do_preload
|
196
197
|
@setup = true
|
197
198
|
end
|
@@ -253,11 +254,21 @@ module Zeitwerk
|
|
253
254
|
# @return [void]
|
254
255
|
def eager_load
|
255
256
|
mutex.synchronize do
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
257
|
+
break if @eager_loaded
|
258
|
+
|
259
|
+
queue = non_ignored_root_dirs
|
260
|
+
while dir = queue.shift
|
261
|
+
each_abspath(dir) do |abspath|
|
262
|
+
if ruby?(abspath)
|
263
|
+
require abspath
|
264
|
+
elsif dir?(abspath)
|
265
|
+
queue << abspath
|
266
|
+
end
|
267
|
+
end
|
260
268
|
end
|
269
|
+
|
270
|
+
disable_tracer
|
271
|
+
@eager_loaded = true
|
261
272
|
end
|
262
273
|
end
|
263
274
|
|
@@ -275,6 +286,10 @@ module Zeitwerk
|
|
275
286
|
# @return [#call, nil]
|
276
287
|
attr_accessor :default_logger
|
277
288
|
|
289
|
+
# @private
|
290
|
+
# @return [Mutex]
|
291
|
+
attr_accessor :mutex
|
292
|
+
|
278
293
|
# This is a shortcut for
|
279
294
|
#
|
280
295
|
# require "zeitwerk"
|
@@ -307,6 +322,8 @@ module Zeitwerk
|
|
307
322
|
end
|
308
323
|
end
|
309
324
|
|
325
|
+
self.mutex = Mutex.new
|
326
|
+
|
310
327
|
# --- Callbacks -------------------------------------------------------------------------------
|
311
328
|
|
312
329
|
# Callback invoked from Kernel when a managed file is loaded.
|
@@ -327,12 +344,14 @@ module Zeitwerk
|
|
327
344
|
# @return [void]
|
328
345
|
def on_dir_loaded(dir)
|
329
346
|
parent, cname = autoloads[dir]
|
330
|
-
loaded.add(cpath(parent, cname))
|
331
|
-
autovivified = parent.const_set(cname, Module.new)
|
332
|
-
log("module #{cpath(parent, cname)} autovivified from directory #{dir}") if logger
|
333
347
|
|
334
|
-
|
335
|
-
|
348
|
+
autovivified_module = parent.const_set(cname, Module.new)
|
349
|
+
log("module #{autovivified_module.name} autovivified from directory #{dir}") if logger
|
350
|
+
|
351
|
+
loaded.add(autovivified_module.name)
|
352
|
+
|
353
|
+
if subdirs = lazy_subdirs[autovivified_module.name]
|
354
|
+
subdirs.each { |subdir| set_autoloads_in_dir(subdir, autovivified_module) }
|
336
355
|
end
|
337
356
|
end
|
338
357
|
|
@@ -340,7 +359,7 @@ module Zeitwerk
|
|
340
359
|
|
341
360
|
# @return [<String>]
|
342
361
|
def non_ignored_root_dirs
|
343
|
-
root_dirs.keys.delete_if { |
|
362
|
+
root_dirs.keys.delete_if { |root_dir| ignored_paths.member?(root_dir) }
|
344
363
|
end
|
345
364
|
|
346
365
|
# @param dir [String]
|
@@ -440,21 +459,6 @@ module Zeitwerk
|
|
440
459
|
parent.autoload?(cname) || Registry.inception?(cpath(parent, cname))
|
441
460
|
end
|
442
461
|
|
443
|
-
# @param dir [String]
|
444
|
-
# @return [void]
|
445
|
-
def eager_load_dir(dir)
|
446
|
-
queue = [dir]
|
447
|
-
while dir = queue.shift
|
448
|
-
each_abspath(dir) do |abspath|
|
449
|
-
if ruby?(abspath)
|
450
|
-
require abspath
|
451
|
-
elsif dir?(abspath)
|
452
|
-
queue << abspath
|
453
|
-
end
|
454
|
-
end
|
455
|
-
end
|
456
|
-
end
|
457
|
-
|
458
462
|
# This method is called this way because I prefer `preload` to be the method
|
459
463
|
# name to configure preloads in the public interface.
|
460
464
|
#
|
@@ -545,5 +549,20 @@ module Zeitwerk
|
|
545
549
|
def cdef?(parent, cname)
|
546
550
|
parent.const_defined?(cname, false)
|
547
551
|
end
|
552
|
+
|
553
|
+
def raise_if_conflicting_directory(dir)
|
554
|
+
Registry.loaders.each do |loader|
|
555
|
+
next if loader == self
|
556
|
+
|
557
|
+
loader.dirs.each do |already_managed_dir|
|
558
|
+
if dir.start_with?(already_managed_dir) || already_managed_dir.start_with?(dir)
|
559
|
+
raise ConflictingDirectory,
|
560
|
+
"loader\n\n\t#{inspect}\n\nwants to manage directory #{dir}," \
|
561
|
+
" which is already managed by\n\n\t#{loader.inspect}"
|
562
|
+
EOS
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
end
|
548
567
|
end
|
549
568
|
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: 1.
|
4
|
+
version: 1.2.0.beta
|
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-02-
|
11
|
+
date: 2019-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
|
@@ -22,6 +22,7 @@ extra_rdoc_files: []
|
|
22
22
|
files:
|
23
23
|
- README.md
|
24
24
|
- lib/zeitwerk.rb
|
25
|
+
- lib/zeitwerk/conflicting_directory.rb
|
25
26
|
- lib/zeitwerk/gem_inflector.rb
|
26
27
|
- lib/zeitwerk/inflector.rb
|
27
28
|
- lib/zeitwerk/kernel.rb
|
@@ -43,9 +44,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
44
|
version: 2.4.4
|
44
45
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
46
|
requirements:
|
46
|
-
- - "
|
47
|
+
- - ">"
|
47
48
|
- !ruby/object:Gem::Version
|
48
|
-
version:
|
49
|
+
version: 1.3.1
|
49
50
|
requirements: []
|
50
51
|
rubygems_version: 3.0.1
|
51
52
|
signing_key:
|