zeitwerk 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 172e4bb51ab526d5f4f606f0c4099cd9d4b8bdfe15a2ec851ed58e7095018f07
4
- data.tar.gz: 74b2d3f010de6374d919e7cae4d759a86490d91bfb7cd3f743b314e10569d2e9
3
+ metadata.gz: 40e2919df324c67a85e00ac31349b96349b1a74a39e97e3f41c513258f108542
4
+ data.tar.gz: 17c8c31d6a89e4ead544d1bc35143f548331923106decb05be67ebf57daf1bce
5
5
  SHA512:
6
- metadata.gz: 57531ede94526489ffb47235b0113ff6cb290c5d21c4b03dbbdea79a55b0e6b74d685b8f5d57f5b0bf9ec85986e731368bd57b9f5dd0ccfdc40033e5b570bbaa
7
- data.tar.gz: d50a101226b04214af4bcc6961422501044a4b9bede6d18bc06a622736bedd7d19fa9b8b9191b8f08042382d0bebcfb8e0162aa7aef37eddd2e17dbcdc6ee8a6
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.setup # ready!
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.
@@ -3,6 +3,7 @@
3
3
  module Zeitwerk
4
4
  require_relative "zeitwerk/loader"
5
5
  require_relative "zeitwerk/registry"
6
+ require_relative "zeitwerk/explicit_namespace"
6
7
  require_relative "zeitwerk/inflector"
7
8
  require_relative "zeitwerk/gem_inflector"
8
9
  require_relative "zeitwerk/kernel"
@@ -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
@@ -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
- enable_tracer if ruby?(autoload_path)
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(parent, cname)] ||= []) << subdir
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
- enable_tracer
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
@@ -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 = []
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "1.3.1"
4
+ VERSION = "1.3.2"
5
5
  end
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.1
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-02-23 00:00:00.000000000 Z
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