zeitwerk 1.3.4 → 1.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53e3caf01e7af10f0f12922ebbe41f400a27eee1fa8d47a1d580ba1bb421393b
4
- data.tar.gz: 6b76087bdc7abec95aa7a70ee5c5b70cb7567e7b02c8dbb290852a3bda523089
3
+ metadata.gz: a7e867060d3bf347938f153335a463ddbb48995f70f3aed8472a6641e2592a71
4
+ data.tar.gz: af10438f14e119bf184133b1c1734e01fbe61954ce2128805a1ab941125dc4cf
5
5
  SHA512:
6
- metadata.gz: 31aabb9e4c66028674508efa6dad871ece2893a3bb78e9c53d24be6721dbbd1a9f0960348ca5c7b33dc3e16670bd2c8b37eea3d813f6ce045b63410a73033a66
7
- data.tar.gz: dc9f42aecb424d7695d48e831ec0ef70159538ded7abf470cc0a26195087aa8a3ce0d3a3a83c8978a88dda8fccb747013db60b0b1b2169e5a6c9ff1b30e94bb8
6
+ metadata.gz: a744a32914b0bd02c07842b448770512d6a8aad66b5524bbe9bb6ef854faeea1ce11e2090bc9a968aebb3501d54de54f65f7927f9d26d4f365a13ad806920e38
7
+ data.tar.gz: 032c50fd06d88be03ee5fae4d7fd6bdbd34780b5dc1f978a8a63a3a9bdda960d417dcb91b40bb82865d3c58b6751cf58506c1bed2454c65c14c6c1d2b5f7bdc7
data/README.md CHANGED
@@ -216,7 +216,14 @@ Zeitwerk instances are able to eager load their managed files:
216
216
  loader.eager_load
217
217
  ```
218
218
 
219
- That skips ignored files and directories.
219
+ That skips [ignored files and directories](#ignoring-parts-of-the-project), and you can also tell Zeitwerk that certain files or directories are autoloadable, but should not be eager loaded:
220
+
221
+ ```ruby
222
+ db_adapters = "#{__dir__}/my_gem/db_adapters"
223
+ loader.do_not_eager_load(db_adapters)
224
+ loader.setup
225
+ loader.eager_load # won't eager load the database adapters
226
+ ```
220
227
 
221
228
  Eager loading is synchronized and idempotent.
222
229
 
@@ -57,6 +57,10 @@ module Zeitwerk
57
57
  @cpaths = {}
58
58
  @mutex = Mutex.new
59
59
  @tracer = TracePoint.new(:class) do |event|
60
+ # If the class is a singleton class, we won't do anything with it so we can bail out immediately.
61
+ # This is several orders of magnitude faster than accessing `Module#name`.
62
+ next if event.self.singleton_class?
63
+
60
64
  # Note that it makes sense to compute the hash code unconditionally,
61
65
  # because the trace point is disabled if cpaths is empty.
62
66
  if loader = cpaths.delete(event.self.name)
@@ -57,7 +57,7 @@ module Zeitwerk
57
57
  #
58
58
  # A shadowed file is a file managed by this autoloader that is skipped
59
59
  # because its matching constant path has already been seen. Think $LOAD_PATH
60
- # + require, only the first occurrence of a given relative name is loaded.
60
+ # and require, only the first occurrence of a given relative name is loaded.
61
61
  #
62
62
  # If the existing occurrence is an autoload, we map the file name to the
63
63
  # shadowing autoload path. If the existing occurrence is an already defined
@@ -66,7 +66,7 @@ module Zeitwerk
66
66
  #
67
67
  # @private
68
68
  # @return [{String => String}]
69
- attr_reader :shadowed
69
+ attr_reader :shadowed_files
70
70
 
71
71
  # Maps real absolute paths for which an autoload has been set to their
72
72
  # corresponding parent class or module and constant name.
@@ -83,7 +83,7 @@ module Zeitwerk
83
83
  #
84
84
  # @private
85
85
  # @return [Set<String>]
86
- attr_reader :loaded
86
+ attr_reader :loaded_cpaths
87
87
 
88
88
  # Maps constant paths of namespaces to arrays of corresponding directories.
89
89
  #
@@ -103,6 +103,12 @@ module Zeitwerk
103
103
  # @return [{String => <String>}]
104
104
  attr_reader :lazy_subdirs
105
105
 
106
+ # Absolute paths of files or directories not to be eager loaded.
107
+ #
108
+ # @private
109
+ # @return [Set<String>]
110
+ attr_reader :eager_load_exclusions
111
+
106
112
  # @private
107
113
  # @return [Mutex]
108
114
  attr_reader :mutex
@@ -114,14 +120,15 @@ module Zeitwerk
114
120
  @inflector = Inflector.new
115
121
  @logger = self.class.default_logger
116
122
 
117
- @root_dirs = {}
118
- @preloads = []
119
- @ignored = Set.new
120
- @ignored_paths = Set.new
121
- @autoloads = {}
122
- @loaded = Set.new
123
- @lazy_subdirs = {}
124
- @shadowed = {}
123
+ @root_dirs = {}
124
+ @preloads = []
125
+ @ignored = Set.new
126
+ @ignored_paths = Set.new
127
+ @autoloads = {}
128
+ @loaded_cpaths = Set.new
129
+ @lazy_subdirs = {}
130
+ @shadowed_files = {}
131
+ @eager_load_exclusions = Set.new
125
132
 
126
133
  @mutex = Mutex.new
127
134
  @setup = false
@@ -230,9 +237,9 @@ module Zeitwerk
230
237
  end
231
238
 
232
239
  autoloads.clear
233
- loaded.clear
240
+ loaded_cpaths.clear
234
241
  lazy_subdirs.clear
235
- shadowed.clear
242
+ shadowed_files.clear
236
243
 
237
244
  Registry.on_unload(self)
238
245
  ExplicitNamespace.unregister(self)
@@ -255,18 +262,21 @@ module Zeitwerk
255
262
 
256
263
  # Eager loads all files in the root directories, recursively. Files do not
257
264
  # need to be in `$LOAD_PATH`, absolute file names are used. Ignored files
258
- # are not eager loaded.
265
+ # are not eager loaded. You can opt-out specifically in specific files and
266
+ # directories with `do_not_eager_load`.
259
267
  #
260
268
  # @return [void]
261
269
  def eager_load
262
270
  mutex.synchronize do
263
271
  break if @eager_loaded
264
272
 
265
- queue = non_ignored_root_dirs
273
+ queue = non_ignored_root_dirs.reject { |dir| eager_load_exclusions.member?(dir) }
266
274
  while dir = queue.shift
267
275
  each_abspath(dir) do |abspath|
276
+ next if eager_load_exclusions.member?(abspath)
277
+
268
278
  if ruby?(abspath)
269
- require abspath unless shadowed.key?(abspath)
279
+ require abspath unless shadowed_files.key?(abspath)
270
280
  elsif dir?(abspath)
271
281
  queue << abspath
272
282
  end
@@ -277,12 +287,21 @@ module Zeitwerk
277
287
  end
278
288
  end
279
289
 
290
+ # Let eager load ignore the given files or directories. The constants
291
+ # defined in those files are still autoloadable.
292
+ #
293
+ # @param paths [<String, Pathname, <String, Pathname>>]
294
+ # @return [void]
295
+ def do_not_eager_load(*paths)
296
+ mutex.synchronize { eager_load_exclusions.merge(expand_paths(paths)) }
297
+ end
298
+
280
299
  # Says if the given constant path has been loaded.
281
300
  #
282
301
  # @param cpath [String]
283
302
  # @return [Boolean]
284
303
  def loaded?(cpath)
285
- loaded.member?(cpath)
304
+ loaded_cpaths.member?(cpath)
286
305
  end
287
306
 
288
307
  # --- Class methods ---------------------------------------------------------------------------
@@ -387,7 +406,7 @@ module Zeitwerk
387
406
  def autoload_file(parent, cname, file)
388
407
  if autoload_path = autoload_for?(parent, cname)
389
408
  # First autoload for a Ruby file wins, just ignore subsequent ones.
390
- shadowed[file] = autoload_path and return if ruby?(autoload_path)
409
+ shadowed_files[file] = autoload_path and return if ruby?(autoload_path)
391
410
 
392
411
  # Override autovivification, we want the namespace to become the
393
412
  # class/module defined in this file.
@@ -397,7 +416,7 @@ module Zeitwerk
397
416
  set_autoload(parent, cname, file)
398
417
  register_explicit_namespace(cpath(parent, cname))
399
418
  elsif cdef?(parent, cname)
400
- shadowed[file] = cpath(parent, cname)
419
+ shadowed_files[file] = cpath(parent, cname)
401
420
  else
402
421
  set_autoload(parent, cname, file)
403
422
  end
@@ -6,7 +6,7 @@ module Zeitwerk::Loader::Callbacks
6
6
  # @return [void]
7
7
  def on_file_autoloaded(file)
8
8
  parent, cname = autoloads[file]
9
- loaded.add(cpath(parent, cname))
9
+ loaded_cpaths.add(cpath(parent, cname))
10
10
  log("constant #{cpath(parent, cname)} loaded from file #{file}") if logger
11
11
  end
12
12
 
@@ -22,7 +22,7 @@ module Zeitwerk::Loader::Callbacks
22
22
  autovivified_module = parent.const_set(cname, Module.new)
23
23
  log("module #{autovivified_module.name} autovivified from directory #{dir}") if logger
24
24
 
25
- loaded.add(autovivified_module.name)
25
+ loaded_cpaths.add(autovivified_module.name)
26
26
  on_namespace_loaded(autovivified_module)
27
27
  end
28
28
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zeitwerk
4
- VERSION = "1.3.4"
4
+ VERSION = "1.4.0"
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.4
4
+ version: 1.4.0
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-03-14 00:00:00.000000000 Z
11
+ date: 2019-03-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zeitwerk implements constant autoloading with Ruby semantics. Each gem