zeitwerk 1.1.0 → 1.2.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|