Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package rubygem-zeitwerk for
openSUSE:Factory checked in at 2022-12-13 18:57:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-zeitwerk (Old)
and /work/SRC/openSUSE:Factory/.rubygem-zeitwerk.new.1835 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-zeitwerk"
Tue Dec 13 18:57:04 2022 rev:15 rq:1042665 version:2.6.6
Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-zeitwerk/rubygem-zeitwerk.changes
2022-10-12 18:26:55.837985598 +0200
+++
/work/SRC/openSUSE:Factory/.rubygem-zeitwerk.new.1835/rubygem-zeitwerk.changes
2022-12-13 18:57:33.535873028 +0100
@@ -1,0 +2,6 @@
+Wed Dec 7 11:39:03 UTC 2022 - Stephan Kulow <[email protected]>
+
+updated to version 2.6.6
+ no changelog found
+
+-------------------------------------------------------------------
Old:
----
zeitwerk-2.6.1.gem
New:
----
zeitwerk-2.6.6.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-zeitwerk.spec ++++++
--- /var/tmp/diff_new_pack.4MXi9d/_old 2022-12-13 18:57:34.067875868 +0100
+++ /var/tmp/diff_new_pack.4MXi9d/_new 2022-12-13 18:57:34.071875889 +0100
@@ -24,7 +24,7 @@
#
Name: rubygem-zeitwerk
-Version: 2.6.1
+Version: 2.6.6
Release: 0
%define mod_name zeitwerk
%define mod_full_name %{mod_name}-%{version}
++++++ zeitwerk-2.6.1.gem -> zeitwerk-2.6.6.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md 2022-10-01 00:18:05.000000000 +0200
+++ new/README.md 2022-11-08 09:02:43.000000000 +0100
@@ -27,8 +27,14 @@
- [Autoloading](#autoloading)
- [Eager loading](#eager-loading)
- [Eager load exclusions](#eager-load-exclusions)
+ - [Eager load directories](#eager-load-directories)
+ - [Eager load namespaces](#eager-load-namespaces)
+ - [Eager load namespaces shared by several
loaders](#eager-load-namespaces-shared-by-several-loaders)
- [Global eager load](#global-eager-load)
+ - [Loading individual files](#loading-individual-files)
- [Reloading](#reloading)
+ - [Configuration and usage](#configuration-and-usage)
+ - [Thread-safety](#thread-safety)
- [Inflection](#inflection)
- [Zeitwerk::Inflector](#zeitwerkinflector)
- [Zeitwerk::GemInflector](#zeitwerkgeminflector)
@@ -44,6 +50,7 @@
- [Use case: Files that do not follow the
conventions](#use-case-files-that-do-not-follow-the-conventions)
- [Use case: The adapter pattern](#use-case-the-adapter-pattern)
- [Use case: Test files mixed with implementation
files](#use-case-test-files-mixed-with-implementation-files)
+ - [Shadowed files](#shadowed-files)
- [Edge cases](#edge-cases)
- [Beware of circular dependencies](#beware-of-circular-dependencies)
- [Reopening third-party namespaces](#reopening-third-party-namespaces)
@@ -206,7 +213,7 @@
<a id="markdown-custom-root-namespaces" name="custom-root-namespaces"></a>
#### Custom root namespaces
-While `Object` is by far the most common root namespace, you can associate a
different one to a particular root directory. The method `push_dir` accepts a
class or module object in the optional `namespace` keyword argument.
+While `Object` is by far the most common root namespace, you can associate a
different one to a particular root directory. The method `push_dir` accepts a
non-anonymous class or module object in the optional `namespace` keyword
argument.
For example, given:
@@ -442,6 +449,8 @@
Eager loading is synchronized and idempotent.
+Attempting to eager load without previously calling `setup` raises
`Zeitwerk::SetupRequired`.
+
<a id="markdown-eager-load-exclusions" name="eager-load-exclusions"></a>
#### Eager load exclusions
@@ -464,6 +473,81 @@
The `force` flag does not affect ignored files and directories, those are
still ignored.
+<a id="markdown-eager-load-directories" name="eager-load-directories"></a>
+#### Eager load directories
+
+The method `Zeitwerk::Loader#eager_load_dir` eager loads a given directory,
recursively:
+
+```ruby
+loader.eager_load_dir("#{__dir__}/custom_web_app/routes")
+```
+
+This is useful when the loader is not eager loading the entire project, but
you still need some subtree to be loaded for things to function properly.
+
+Both strings and `Pathname` objects are supported as arguments. If the
argument is not a directory managed by the receiver, the method raises
`Zeitwerk::Error`.
+
+[Eager load exclusions](#eager-load-exclusions), [ignored files and
directories](#ignoring-parts-of-the-project), and [shadowed
files](https://github.com/fxn/zeitwerk#shadowed-files) are not eager loaded.
+
+`Zeitwerk::Loader#eager_load_dir` is idempotent, but compatible with
reloading. If you eager load a directory and then reload, eager loading that
directory will load its (current) contents again.
+
+The method checks if a regular eager load was already executed, in which case
it returns fast.
+
+Nested root directories which are descendants of the argument are skipped.
Those subtrees are considered to be conceptually apart.
+
+Attempting to eager load a directory without previously calling `setup` raises
`Zeitwerk::SetupRequired`.
+
+<a id="markdown-eager-load-namespaces" name="eager-load-namespaces"></a>
+#### Eager load namespaces
+
+The method `Zeitwerk::Loader#eager_load_namespace` eager loads a given
namespace, recursively:
+
+```ruby
+loader.eager_load_namespace(MyApp::Routes)
+```
+
+This is useful when the loader is not eager loading the entire project, but
you still need some namespace to be loaded for things to function properly.
+
+The argument has to be a class or module object and the method raises
`Zeitwerk::Error` otherwise.
+
+If the namespace is spread over multiple directories in the receiver's source
tree, they are all eager loaded. For example, if you have a structure like
+
+```
+root_dir1/my_app/routes
+root_dir2/my_app/routes
+root_dir3/my_app/routes
+```
+
+where `root_directory{1,2,3}` are root directories, eager loading
`MyApp::Routes` will eager load the contents of the three corresponding
directories.
+
+There might exist external source trees implementing part of the namespace.
This happens routinely, because top-level constants are stored in the globally
shared `Object`. It happens also when deliberately [reopening third-party
namespaces](reopening-third-party-namespaces). Such external code is not eager
loaded, the implementation is carefully scoped to what the receiver manages to
avoid side-effects elsewhere.
+
+This method is flexible about what it accepts. Its semantics have to be
interpreted as: "_If_ you manage this namespace, or part of this namespace,
please eager load what you got". In particular, if the receiver does not manage
the namespace, it will simply do nothing, this is not an error condition.
+
+[Eager load exclusions](#eager-load-exclusions), [ignored files and
directories](#ignoring-parts-of-the-project), and [shadowed
files](https://github.com/fxn/zeitwerk#shadowed-files) are not eager loaded.
+
+`Zeitwerk::Loader#eager_load_namespace` is idempotent, but compatible with
reloading. If you eager load a namespace and then reload, eager loading that
namespace will load its (current) descendants again.
+
+The method checks if a regular eager load was already executed, in which case
it returns fast.
+
+If root directories are assigned to custom namespaces, the method behaves as
you'd expect, according to the namespacing relationship between the custom
namespace and the argument.
+
+Attempting to eager load a namespace without previously calling `setup` raises
`Zeitwerk::SetupRequired`.
+
+<a id="markdown-eager-load-namespaces-shared-by-several-loaders"
name="eager-load-namespaces-shared-by-several-loaders"></a>
+#### Eager load namespaces shared by several loaders
+
+The method `Zeitwerk::Loader.eager_load_namespace` broadcasts
`eager_load_namespace` to all loaders.
+
+```ruby
+Zeitwerk::Loader.eager_load_namespace(MyFramework::Routes)
+```
+
+This may be handy, for example, if a framework supports plugins and a shared
namespace needs to be eager loaded for the framework to function properly.
+
+Please, note that loaders only eager load namespaces they manage, as
documented above. Therefore, this method does not allow you to eager load
namespaces not managed by Zeitwerk loaders.
+
+This method does not require that all registered loaders have `setup` already
invoked, since that is out of your control. If there's any in that state, it is
simply skipped.
+
<a id="markdown-global-eager-load" name="global-eager-load"></a>
#### Global eager load
@@ -479,9 +563,31 @@
This method does not accept the `force` flag, since in general it wouldn't be
a good idea to force eager loading in 3rd party code.
+This method does not require that all registered loaders have `setup` already
invoked, since that is out of your control. If there's any in that state, it is
simply skipped.
+
+<a id="markdown-loading-individual-files" name="loading-individual-files"></a>
+### Loading individual files
+
+The method `Zeitwerk::Loader#load_file` loads an individual Ruby file:
+
+```ruby
+loader.load_file("#{__dir__}/custom_web_app/routes.rb")
+```
+
+This is useful when the loader is not eager loading the entire project, but
you still need an individual file to be loaded for things to function properly.
+
+Both strings and `Pathname` objects are supported as arguments. The method
raises `Zeitwerk::Error` if the argument is not a Ruby file, is
[ignored](#ignoring-parts-of-the-project), is
[shadowed](https://github.com/fxn/zeitwerk#shadowed-files), or is not managed
by the receiver.
+
+`Zeitwerk::Loader#load_file` is idempotent, but compatible with reloading. If
you load a file and then reload, a new call will load its (current) contents
again.
+
+If you want to eager load a directory, `Zeitwerk::Loader#eager_load_dir` is
more efficient than invoking `Zeitwerk::Loader#load_file` on its files.
+
<a id="markdown-reloading" name="reloading"></a>
### Reloading
+<a id="markdown-configuration-and-usage" name="configuration-and-usage"></a>
+#### Configuration and usage
+
Zeitwerk is able to reload code, but you need to enable this feature:
```ruby
@@ -495,7 +601,7 @@
There is no way to undo this, either you want to reload or you don't.
-Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload
without having it enabled raises `Zeitwerk::ReloadingDisabledError`.
+Enabling reloading after setup raises `Zeitwerk::Error`. Attempting to reload
without having it enabled raises `Zeitwerk::ReloadingDisabledError`. Attempting
to reload without previously calling `setup` raises `Zeitwerk::SetupRequired`.
Generally speaking, reloading is useful while developing running services like
web applications. Gems that implement regular libraries, so to speak, or
services running in testing or production environments, won't normally have a
use case for reloading. If reloading is not enabled, Zeitwerk is able to use
less memory.
@@ -503,12 +609,34 @@
It is important to highlight that this is an instance method. Don't worry
about project dependencies managed by Zeitwerk, their loaders are independent.
-Reloading is not thread-safe:
+<a id="markdown-thread-safety" name="thread-safety"></a>
+#### Thread-safety
-* You should not reload while another thread is reloading.
-* You should not autoload while another thread is reloading.
+In order to reload safely, no other thread can be autoloading or reloading
concurrently. Client code is responsible for this coordination.
-In order to reload in a thread-safe manner, frameworks need to implement some
coordination. For example, a web framework that serves each request with its
own thread may have a globally accessible read/write lock: When a request comes
in, the framework acquires the lock for reading at the beginning, and releases
it at the end. On the other hand, the code in the framework responsible for the
call to `Zeitwerk::Loader#reload` needs to acquire the lock for writing.
+For example, a web framework that serves each request in its own thread and
has reloading enabled could create a read-write lock on boot like this:
+
+```ruby
+require "concurrent/atomic/read_write_lock"
+
+MyFramework::RELOAD_RW_LOCK = Concurrent::ReadWriteLock.new
+```
+
+You acquire the lock for reading for serving each individual request:
+
+```ruby
+MyFramework::RELOAD_RW_LOCK.with_read_lock do
+ serve(request)
+end
+```
+
+Then, when a reload is triggered, just acquire the lock for writing in order
to execute the method call safely:
+
+```ruby
+MyFramework::RELOAD_RW_LOCK.with_write_lock do
+ loader.reload
+end
+```
On reloading, client code has to update anything that would otherwise be
storing a stale object. For example, if the routing layer of a web framework
stores reloadable controller class objects or instances in internal structures,
on reload it has to refresh them somehow, possibly reevaluating routes.
@@ -905,10 +1033,39 @@
loader.setup
```
+<a id="markdown-shadowed-files" name="shadowed-files"></a>
+### Shadowed files
+
+In Ruby, if you have several files called `foo.rb` in different directories of
`$LOAD_PATH` and execute
+
+```ruby
+require "foo"
+```
+
+the first one found gets loaded, and the rest are ignored.
+
+Zeitwerk behaves in a similar way. If `foo.rb` is present in several root
directories (at the same namespace level), the constant `Foo` is autoloaded
from the first one, and the rest of the files are not evaluated. If logging is
enabled, you'll see something like
+
+```
+file #{file} is ignored because #{previous_occurrence} has precedence
+```
+
+(This message is not public interface and may change, you cannot rely on that
exact wording.)
+
+Even if there's only one `foo.rb`, if the constant `Foo` is already defined
when Zeitwerk finds `foo.rb`, then the file is ignored too. This could happen
if `Foo` was defined by a dependency, for example. If logging is enabled,
you'll see something like
+
+```
+file #{file} is ignored because #{constant_path} is already defined
+```
+
+(This message is not public interface and may change, you cannot rely on that
exact wording.)
+
+Shadowing only applies to Ruby files, namespace definition can be spread over
multiple directories. And you can also reopen third-party namespaces if done
[orderly](#reopening-third-party-namespaces).
+
<a id="markdown-edge-cases" name="edge-cases"></a>
### Edge cases
-A class or module that acts as a namespace:
+[Explicit namespaces](#explicit-namespaces) like `Trip` here:
```ruby
# trip.rb
@@ -922,7 +1079,7 @@
end
```
-has to be defined with the `class` or `module` keywords, as in the example
above.
+have to be defined with the `class`/`module` keywords, as in the example above.
For technical reasons, raw constant assignment is not supported:
@@ -984,7 +1141,7 @@
require "active_job/queue_adapters"
require "zeitwerk"
-# By passign the flag, we acknowledge the extra directory lib/active_job
+# By passing the flag, we acknowledge the extra directory lib/active_job
# has to be managed by the loader and no warning has to be issued for it.
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
loader.setup
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/error.rb new/lib/zeitwerk/error.rb
--- old/lib/zeitwerk/error.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/error.rb 2022-11-08 09:02:43.000000000 +0100
@@ -12,4 +12,10 @@
class NameError < ::NameError
end
+
+ class SetupRequired < Error
+ def initialize
+ super("please, finish your configuration and call Zeitwerk::Loader#setup
once all is ready")
+ end
+ end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/explicit_namespace.rb
new/lib/zeitwerk/explicit_namespace.rb
--- old/lib/zeitwerk/explicit_namespace.rb 2022-10-01 00:18:05.000000000
+0200
+++ new/lib/zeitwerk/explicit_namespace.rb 2022-11-08 09:02:43.000000000
+0100
@@ -11,28 +11,28 @@
module ExplicitNamespace # :nodoc: all
class << self
include RealModName
+ extend Internal
# Maps constant paths that correspond to explicit namespaces according to
# the file system, to the loader responsible for them.
#
- # @private
# @sig Hash[String, Zeitwerk::Loader]
attr_reader :cpaths
+ private :cpaths
- # @private
# @sig Mutex
attr_reader :mutex
+ private :mutex
- # @private
# @sig TracePoint
attr_reader :tracer
+ private :tracer
# Asserts `cpath` corresponds to an explicit namespace for which `loader`
# is responsible.
#
- # @private
# @sig (String, Zeitwerk::Loader) -> void
- def register(cpath, loader)
+ internal def register(cpath, loader)
mutex.synchronize do
cpaths[cpath] = loader
# We check enabled? because, looking at the C source code, enabling
an
@@ -41,24 +41,21 @@
end
end
- # @private
# @sig (Zeitwerk::Loader) -> void
- def unregister_loader(loader)
+ internal def unregister_loader(loader)
cpaths.delete_if { |_cpath, l| l == loader }
disable_tracer_if_unneeded
end
- private
-
# @sig () -> void
- def disable_tracer_if_unneeded
+ private def disable_tracer_if_unneeded
mutex.synchronize do
tracer.disable if cpaths.empty?
end
end
# @sig (TracePoint) -> void
- def tracepoint_class_callback(event)
+ private def tracepoint_class_callback(event)
# If the class is a singleton class, we won't do anything with it so we
# can bail out immediately. This is several orders of magnitude faster
# than accessing its name.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/gem_loader.rb
new/lib/zeitwerk/gem_loader.rb
--- old/lib/zeitwerk/gem_loader.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/gem_loader.rb 2022-11-08 09:02:43.000000000 +0100
@@ -43,7 +43,7 @@
next if abspath == expected_namespace_dir
basename_without_ext = basename.delete_suffix(".rb")
- cname = inflector.camelize(basename_without_ext, abspath)
+ cname = inflector.camelize(basename_without_ext, abspath).to_sym
ftype = dir?(abspath) ? "directory" : "file"
warn(<<~EOS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/internal.rb new/lib/zeitwerk/internal.rb
--- old/lib/zeitwerk/internal.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/lib/zeitwerk/internal.rb 2022-11-08 09:02:43.000000000 +0100
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# This is a private module.
+module Zeitwerk::Internal
+ def internal(method_name)
+ private method_name
+
+ mangled = "__#{method_name}"
+ alias_method mangled, method_name
+ public mangled
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/kernel.rb new/lib/zeitwerk/kernel.rb
--- old/lib/zeitwerk/kernel.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/kernel.rb 2022-11-08 09:02:43.000000000 +0100
@@ -19,6 +19,9 @@
# included in Object, and changes in ancestors don't get propagated into
# already existing ancestor chains on Ruby < 3.0.
alias_method :zeitwerk_original_require, :require
+ class << self
+ alias_method :zeitwerk_original_require, :require
+ end
# @sig (String) -> true | false
def require(path)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/loader/callbacks.rb
new/lib/zeitwerk/loader/callbacks.rb
--- old/lib/zeitwerk/loader/callbacks.rb 2022-10-01 00:18:05.000000000
+0200
+++ new/lib/zeitwerk/loader/callbacks.rb 2022-11-08 09:02:43.000000000
+0100
@@ -42,7 +42,7 @@
# Without the mutex and subsequent delete call, t2 would reset the module.
# That not only would reassign the constant (undesirable per se) but,
worse,
# the module object created by t2 wouldn't have any of the autoloads for
its
- # children, since t1 would have correctly deleted its lazy_subdirs entry.
+ # children, since t1 would have correctly deleted its namespace_dirs entry.
mutex2.synchronize do
if cref = autoloads.delete(dir)
autovivified_module = cref[0].const_set(cref[1], Module.new)
@@ -71,9 +71,9 @@
# @private
# @sig (Module) -> void
def on_namespace_loaded(namespace)
- if subdirs = lazy_subdirs.delete(real_mod_name(namespace))
- subdirs.each do |subdir|
- set_autoloads_in_dir(subdir, namespace)
+ if dirs = namespace_dirs.delete(real_mod_name(namespace))
+ dirs.each do |dir|
+ set_autoloads_in_dir(dir, namespace)
end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/loader/config.rb
new/lib/zeitwerk/loader/config.rb
--- old/lib/zeitwerk/loader/config.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/loader/config.rb 2022-11-08 09:02:43.000000000 +0100
@@ -4,86 +4,91 @@
require "securerandom"
module Zeitwerk::Loader::Config
- # Absolute paths of the root directories. Stored in a hash to preserve
- # order, easily handle duplicates, have a fast lookup needed for detecting
- # nested paths, and store custom namespaces as values.
+ extend Zeitwerk::Internal
+ include Zeitwerk::RealModName
+
+ # @sig #camelize
+ attr_accessor :inflector
+
+ # @sig #call | #debug | nil
+ attr_accessor :logger
+
+ # Absolute paths of the root directories, mapped to their respective root
namespaces:
#
- # "/Users/fxn/blog/app/assets" => Object,
# "/Users/fxn/blog/app/channels" => Object,
- # "/Users/fxn/blog/adapters" => ActiveJob::QueueAdapters,
+ # "/Users/fxn/blog/app/adapters" => ActiveJob::QueueAdapters,
# ...
#
+ # Stored in a hash to preserve order, easily handle duplicates, and have a
+ # fast lookup by directory.
+ #
# This is a private collection maintained by the loader. The public
# interface for it is `push_dir` and `dirs`.
#
- # @private
# @sig Hash[String, Module]
- attr_reader :root_dirs
-
- # @sig #camelize
- attr_accessor :inflector
+ attr_reader :roots
+ internal :roots
# Absolute paths of files, directories, or glob patterns to be totally
# ignored.
#
- # @private
# @sig Set[String]
attr_reader :ignored_glob_patterns
+ private :ignored_glob_patterns
# The actual collection of absolute file and directory names at the time the
# ignored glob patterns were expanded. Computed on setup, and recomputed on
# reload.
#
- # @private
# @sig Set[String]
attr_reader :ignored_paths
+ private :ignored_paths
# Absolute paths of directories or glob patterns to be collapsed.
#
- # @private
# @sig Set[String]
attr_reader :collapse_glob_patterns
+ private :collapse_glob_patterns
# The actual collection of absolute directory names at the time the collapse
# glob patterns were expanded. Computed on setup, and recomputed on reload.
#
- # @private
# @sig Set[String]
attr_reader :collapse_dirs
+ private :collapse_dirs
# Absolute paths of files or directories not to be eager loaded.
#
- # @private
# @sig Set[String]
attr_reader :eager_load_exclusions
+ private :eager_load_exclusions
# User-oriented callbacks to be fired on setup and on reload.
#
- # @private
# @sig Array[{ () -> void }]
attr_reader :on_setup_callbacks
+ private :on_setup_callbacks
# User-oriented callbacks to be fired when a constant is loaded.
#
- # @private
# @sig Hash[String, Array[{ (Object, String) -> void }]]
# Hash[Symbol, Array[{ (String, Object, String) -> void }]]
attr_reader :on_load_callbacks
+ private :on_load_callbacks
# User-oriented callbacks to be fired before constants are removed.
#
- # @private
# @sig Hash[String, Array[{ (Object, String) -> void }]]
# Hash[Symbol, Array[{ (String, Object, String) -> void }]]
attr_reader :on_unload_callbacks
-
- # @sig #call | #debug | nil
- attr_accessor :logger
+ private :on_unload_callbacks
def initialize
- @initialized_at = Time.now
- @root_dirs = {}
@inflector = Zeitwerk::Inflector.new
+ @logger = self.class.default_logger
+ @tag = SecureRandom.hex(3)
+ @initialized_at = Time.now
+ @roots = {}
@ignored_glob_patterns = Set.new
@ignored_paths = Set.new
@collapse_glob_patterns = Set.new
@@ -93,8 +98,6 @@
@on_setup_callbacks = []
@on_load_callbacks = {}
@on_unload_callbacks = {}
- @logger = self.class.default_logger
- @tag = SecureRandom.hex(3)
end
# Pushes `path` to the list of root directories.
@@ -111,10 +114,14 @@
raise Zeitwerk::Error, "#{namespace.inspect} is not a class or module
object, should be"
end
+ unless real_mod_name(namespace)
+ raise Zeitwerk::Error, "root namespaces cannot be anonymous"
+ end
+
abspath = File.expand_path(path)
if dir?(abspath)
raise_if_conflicting_directory(abspath)
- root_dirs[abspath] = namespace
+ roots[abspath] = namespace
else
raise Zeitwerk::Error, "the root directory #{abspath} does not exist"
end
@@ -147,9 +154,9 @@
# @sig () -> Array[String] | Hash[String, Module]
def dirs(namespaces: false)
if namespaces
- root_dirs.clone
+ roots.clone
else
- root_dirs.keys
+ roots.keys
end.freeze
end
@@ -273,57 +280,76 @@
@logger = ->(msg) { puts msg }
end
- # @private
+ # Returns true if the argument has been configured to be ignored, or is a
+ # descendant of an ignored directory.
+ #
# @sig (String) -> bool
- def ignores?(abspath)
- ignored_paths.any? do |ignored_path|
- ignored_path == abspath || (dir?(ignored_path) &&
abspath.start_with?(ignored_path + "/"))
+ internal def ignores?(abspath)
+ # Common use case.
+ return false if ignored_paths.empty?
+
+ walk_up(abspath) do |abspath|
+ return true if ignored_path?(abspath)
+ return false if roots.key?(abspath)
end
+
+ false
end
- private
+ # @sig (String) -> bool
+ private def ignored_path?(abspath)
+ ignored_paths.member?(abspath)
+ end
# @sig () -> Array[String]
- def actual_root_dirs
- root_dirs.reject do |root_dir, _namespace|
- !dir?(root_dir) || ignored_paths.member?(root_dir)
+ private def actual_roots
+ roots.reject do |root_dir, _root_namespace|
+ !dir?(root_dir) || ignored_path?(root_dir)
end
end
# @sig (String) -> bool
- def root_dir?(dir)
- root_dirs.key?(dir)
+ private def root_dir?(dir)
+ roots.key?(dir)
end
# @sig (String) -> bool
- def excluded_from_eager_load?(abspath)
- eager_load_exclusions.member?(abspath)
+ private def excluded_from_eager_load?(abspath)
+ # Optimize this common use case.
+ return false if eager_load_exclusions.empty?
+
+ walk_up(abspath) do |abspath|
+ return true if eager_load_exclusions.member?(abspath)
+ return false if roots.key?(abspath)
+ end
+
+ false
end
# @sig (String) -> bool
- def collapse?(dir)
+ private def collapse?(dir)
collapse_dirs.member?(dir)
end
# @sig (String | Pathname | Array[String | Pathname]) -> Array[String]
- def expand_paths(paths)
+ private def expand_paths(paths)
paths.flatten.map! { |path| File.expand_path(path) }
end
# @sig (Array[String]) -> Array[String]
- def expand_glob_patterns(glob_patterns)
+ private def expand_glob_patterns(glob_patterns)
# Note that Dir.glob works with regular file names just fine. That is,
# glob patterns technically need no wildcards.
glob_patterns.flat_map { |glob_pattern| Dir.glob(glob_pattern) }
end
# @sig () -> void
- def recompute_ignored_paths
+ private def recompute_ignored_paths
ignored_paths.replace(expand_glob_patterns(ignored_glob_patterns))
end
# @sig () -> void
- def recompute_collapse_dirs
+ private def recompute_collapse_dirs
collapse_dirs.replace(expand_glob_patterns(collapse_glob_patterns))
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/loader/eager_load.rb
new/lib/zeitwerk/loader/eager_load.rb
--- old/lib/zeitwerk/loader/eager_load.rb 1970-01-01 01:00:00.000000000
+0100
+++ new/lib/zeitwerk/loader/eager_load.rb 2022-11-08 09:02:43.000000000
+0100
@@ -0,0 +1,228 @@
+module Zeitwerk::Loader::EagerLoad
+ # Eager loads all files in the root directories, recursively. Files do not
+ # need to be in `$LOAD_PATH`, absolute file names are used. Ignored and
+ # shadowed files are not eager loaded. You can opt-out specifically in
+ # specific files and directories with `do_not_eager_load`, and that can be
+ # overridden passing `force: true`.
+ #
+ # @sig (true | false) -> void
+ def eager_load(force: false)
+ mutex.synchronize do
+ break if @eager_loaded
+ raise Zeitwerk::SetupRequired unless @setup
+
+ log("eager load start") if logger
+
+ actual_roots.each do |root_dir, root_namespace|
+ actual_eager_load_dir(root_dir, root_namespace, force: force)
+ end
+
+ autoloaded_dirs.each do |autoloaded_dir|
+ Zeitwerk::Registry.unregister_autoload(autoloaded_dir)
+ end
+ autoloaded_dirs.clear
+
+ @eager_loaded = true
+
+ log("eager load end") if logger
+ end
+ end
+
+ # @sig (String | Pathname) -> void
+ def eager_load_dir(path)
+ raise Zeitwerk::SetupRequired unless @setup
+
+ abspath = File.expand_path(path)
+
+ raise Zeitwerk::Error.new("#{abspath} is not a directory") unless
dir?(abspath)
+
+ cnames = []
+
+ root_namespace = nil
+ walk_up(abspath) do |dir|
+ return if ignored_path?(dir)
+ return if eager_load_exclusions.member?(dir)
+
+ break if root_namespace = roots[dir]
+
+ unless collapse?(dir)
+ basename = File.basename(dir)
+ cnames << inflector.camelize(basename, dir).to_sym
+ end
+ end
+
+ raise Zeitwerk::Error.new("I do not manage #{abspath}") unless
root_namespace
+
+ return if @eager_loaded
+
+ namespace = root_namespace
+ cnames.reverse_each do |cname|
+ # Can happen if there are no Ruby files. This is not an error condition,
+ # the directory is actually managed. Could have Ruby files later.
+ return unless cdef?(namespace, cname)
+ namespace = cget(namespace, cname)
+ end
+
+ # A shortcircuiting test depends on the invocation of this method. Please
+ # keep them in sync if refactored.
+ actual_eager_load_dir(abspath, namespace)
+ end
+
+ # @sig (Module) -> void
+ def eager_load_namespace(mod)
+ raise Zeitwerk::SetupRequired unless @setup
+
+ unless mod.is_a?(Module)
+ raise Zeitwerk::Error, "#{mod.inspect} is not a class or module object"
+ end
+
+ return if @eager_loaded
+
+ mod_name = real_mod_name(mod)
+ return unless mod_name
+
+ actual_roots.each do |root_dir, root_namespace|
+ if mod.equal?(Object)
+ # A shortcircuiting test depends on the invocation of this method.
+ # Please keep them in sync if refactored.
+ actual_eager_load_dir(root_dir, root_namespace)
+ elsif root_namespace.equal?(Object)
+ eager_load_child_namespace(mod, mod_name, root_dir, root_namespace)
+ else
+ root_namespace_name = real_mod_name(root_namespace)
+ if root_namespace_name.start_with?(mod_name + "::")
+ actual_eager_load_dir(root_dir, root_namespace)
+ elsif mod_name == root_namespace_name
+ actual_eager_load_dir(root_dir, root_namespace)
+ elsif mod_name.start_with?(root_namespace_name + "::")
+ eager_load_child_namespace(mod, mod_name, root_dir, root_namespace)
+ else
+ # Unrelated constant hierarchies, do nothing.
+ end
+ end
+ end
+ end
+
+ # Loads the given Ruby file.
+ #
+ # Raises if the argument is ignored, shadowed, or not managed by the
receiver.
+ #
+ # The method is implemented as `constantize` for files, in a sense, to be
able
+ # to descend orderly and make sure the file is loadable.
+ #
+ # @sig (String | Pathname) -> void
+ def load_file(path)
+ abspath = File.expand_path(path)
+
+ raise Zeitwerk::Error.new("#{abspath} does not exist") unless
File.exist?(abspath)
+ raise Zeitwerk::Error.new("#{abspath} is not a Ruby file") if
dir?(abspath) || !ruby?(abspath)
+ raise Zeitwerk::Error.new("#{abspath} is ignored") if
ignored_path?(abspath)
+
+ basename = File.basename(abspath, ".rb")
+ base_cname = inflector.camelize(basename, abspath).to_sym
+
+ root_namespace = nil
+ cnames = []
+
+ walk_up(File.dirname(abspath)) do |dir|
+ raise Zeitwerk::Error.new("#{abspath} is ignored") if ignored_path?(dir)
+
+ break if root_namespace = roots[dir]
+
+ unless collapse?(dir)
+ basename = File.basename(dir)
+ cnames << inflector.camelize(basename, dir).to_sym
+ end
+ end
+
+ raise Zeitwerk::Error.new("I do not manage #{abspath}") unless
root_namespace
+
+ namespace = root_namespace
+ cnames.reverse_each do |cname|
+ namespace = cget(namespace, cname)
+ end
+
+ raise Zeitwerk::Error.new("#{abspath} is shadowed") if
shadowed_file?(abspath)
+
+ cget(namespace, base_cname)
+ end
+
+ # The caller is responsible for making sure `namespace` is the namespace that
+ # corresponds to `dir`.
+ #
+ # @sig (String, Module, Boolean) -> void
+ private def actual_eager_load_dir(dir, namespace, force: false)
+ honour_exclusions = !force
+ return if honour_exclusions && excluded_from_eager_load?(dir)
+
+ log("eager load directory #{dir} start") if logger
+
+ queue = [[dir, namespace]]
+ while to_eager_load = queue.shift
+ dir, namespace = to_eager_load
+
+ ls(dir) do |basename, abspath|
+ next if honour_exclusions && eager_load_exclusions.member?(abspath)
+
+ if ruby?(abspath)
+ if (cref = autoloads[abspath]) && !shadowed_file?(abspath)
+ cget(*cref)
+ end
+ else
+ if collapse?(abspath)
+ queue << [abspath, namespace]
+ else
+ cname = inflector.camelize(basename, abspath).to_sym
+ queue << [abspath, cget(namespace, cname)]
+ end
+ end
+ end
+ end
+
+ log("eager load directory #{dir} end") if logger
+ end
+
+ # In order to invoke this method, the caller has to ensure `child` is a
+ # strict namespace descendendant of `root_namespace`.
+ #
+ # @sig (Module, String, Module, Boolean) -> void
+ private def eager_load_child_namespace(child, child_name, root_dir,
root_namespace)
+ suffix = child_name
+ unless root_namespace.equal?(Object)
+ suffix = suffix.delete_prefix(real_mod_name(root_namespace) + "::")
+ end
+
+ # These directories are at the same namespace level, there may be more if
+ # we find collapsed ones. As we scan, we look for matches for the first
+ # segment, and store them in `next_dirs`. If there are any, we look for
+ # the next segments in those matches. Repeat.
+ #
+ # If we exhaust the search locating directories that match all segments,
+ # we just need to eager load those ones.
+ dirs = [root_dir]
+ next_dirs = []
+
+ suffix.split("::").each do |segment|
+ while dir = dirs.shift
+ ls(dir) do |basename, abspath|
+ next unless dir?(abspath)
+
+ if collapse?(abspath)
+ dirs << abspath
+ elsif segment == inflector.camelize(basename, abspath)
+ next_dirs << abspath
+ end
+ end
+ end
+
+ return if next_dirs.empty?
+
+ dirs.replace(next_dirs)
+ next_dirs.clear
+ end
+
+ dirs.each do |dir|
+ actual_eager_load_dir(dir, child)
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/loader/helpers.rb
new/lib/zeitwerk/loader/helpers.rb
--- old/lib/zeitwerk/loader/helpers.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/loader/helpers.rb 2022-11-08 09:02:43.000000000 +0100
@@ -1,12 +1,10 @@
# frozen_string_literal: true
module Zeitwerk::Loader::Helpers
- private
-
# --- Logging
-----------------------------------------------------------------------------------
# @sig (String) -> void
- def log(message)
+ private def log(message)
method_name = logger.respond_to?(:debug) ? :debug : :call
logger.send(method_name, "Zeitwerk@#{tag}: #{message}")
end
@@ -14,7 +12,7 @@
# --- Files and directories
---------------------------------------------------------------------
# @sig (String) { (String, String) -> void } -> void
- def ls(dir)
+ private def ls(dir)
children = Dir.children(dir)
# The order in which a directory is listed depends on the file system.
@@ -28,10 +26,11 @@
next if hidden?(basename)
abspath = File.join(dir, basename)
- next if ignored_paths.member?(abspath)
+ next if ignored_path?(abspath)
if dir?(abspath)
- next unless has_at_least_one_ruby_file?(abspath)
+ next if roots.key?(abspath)
+ next if !has_at_least_one_ruby_file?(abspath)
else
next unless ruby?(abspath)
end
@@ -43,7 +42,7 @@
end
# @sig (String) -> bool
- def has_at_least_one_ruby_file?(dir)
+ private def has_at_least_one_ruby_file?(dir)
to_visit = [dir]
while dir = to_visit.shift
@@ -60,20 +59,29 @@
end
# @sig (String) -> bool
- def ruby?(path)
+ private def ruby?(path)
path.end_with?(".rb")
end
# @sig (String) -> bool
- def dir?(path)
+ private def dir?(path)
File.directory?(path)
end
# @sig (String) -> bool
- def hidden?(basename)
+ private def hidden?(basename)
basename.start_with?(".")
end
+ # @sig (String) { (String) -> void } -> void
+ private def walk_up(abspath)
+ loop do
+ yield abspath
+ abspath, basename = File.split(abspath)
+ break if basename == "/"
+ end
+ end
+
# --- Constants
---------------------------------------------------------------------------------
# The autoload? predicate takes into account the ancestor chain of the
@@ -94,11 +102,11 @@
#
# @sig (Module, Symbol) -> String?
if method(:autoload?).arity == 1
- def strict_autoload_path(parent, cname)
+ private def strict_autoload_path(parent, cname)
parent.autoload?(cname) if cdef?(parent, cname)
end
else
- def strict_autoload_path(parent, cname)
+ private def strict_autoload_path(parent, cname)
parent.autoload?(cname, false)
end
end
@@ -107,23 +115,23 @@
if Symbol.method_defined?(:name)
# Symbol#name was introduced in Ruby 3.0. It returns always the same
# frozen object, so we may save a few string allocations.
- def cpath(parent, cname)
+ private def cpath(parent, cname)
Object == parent ? cname.name : "#{real_mod_name(parent)}::#{cname.name}"
end
else
- def cpath(parent, cname)
+ private def cpath(parent, cname)
Object == parent ? cname.to_s : "#{real_mod_name(parent)}::#{cname}"
end
end
# @sig (Module, Symbol) -> bool
- def cdef?(parent, cname)
+ private def cdef?(parent, cname)
parent.const_defined?(cname, false)
end
# @raise [NameError]
# @sig (Module, Symbol) -> Object
- def cget(parent, cname)
+ private def cget(parent, cname)
parent.const_get(cname, false)
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/loader.rb new/lib/zeitwerk/loader.rb
--- old/lib/zeitwerk/loader.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/loader.rb 2022-11-08 09:02:43.000000000 +0100
@@ -7,11 +7,13 @@
require_relative "loader/helpers"
require_relative "loader/callbacks"
require_relative "loader/config"
+ require_relative "loader/eager_load"
include RealModName
include Callbacks
include Helpers
include Config
+ include EagerLoad
MUTEX = Mutex.new
private_constant :MUTEX
@@ -54,7 +56,7 @@
# @sig Hash[String, [String, [Module, Symbol]]]
attr_reader :to_unload
- # Maps constant paths of namespaces to arrays of corresponding directories.
+ # Maps namespace constant paths to their respective directories.
#
# For example, given this mapping:
#
@@ -64,13 +66,24 @@
# ...
# ]
#
- # when `Admin` gets defined we know that it plays the role of a namespace
and
- # that its children are spread over those directories. We'll visit them to
set
- # up the corresponding autoloads.
+ # when `Admin` gets defined we know that it plays the role of a namespace
+ # and that its children are spread over those directories. We'll visit them
+ # to set up the corresponding autoloads.
#
# @private
# @sig Hash[String, Array[String]]
- attr_reader :lazy_subdirs
+ attr_reader :namespace_dirs
+
+ # A shadowed file is a file managed by this loader that is ignored when
+ # setting autoloads because its matching constant is already taken.
+ #
+ # This private set is populated as we descend. For example, if the loader
+ # has only scanned the top-level, `shadowed_files` does not have shadowed
+ # files that may exist deep in the project tree yet.
+ #
+ # @private
+ # @sig Set[String]
+ attr_reader :shadowed_files
# @private
# @sig Mutex
@@ -86,7 +99,8 @@
@autoloads = {}
@autoloaded_dirs = []
@to_unload = {}
- @lazy_subdirs = Hash.new { |h, cpath| h[cpath] = [] }
+ @namespace_dirs = Hash.new { |h, cpath| h[cpath] = [] }
+ @shadowed_files = Set.new
@mutex = Mutex.new
@mutex2 = Mutex.new
@setup = false
@@ -95,15 +109,15 @@
Registry.register_loader(self)
end
- # Sets autoloads in the root namespace.
+ # Sets autoloads in the root namespaces.
#
# @sig () -> void
def setup
mutex.synchronize do
break if @setup
- actual_root_dirs.each do |root_dir, namespace|
- set_autoloads_in_dir(root_dir, namespace)
+ actual_roots.each do |root_dir, root_namespace|
+ set_autoloads_in_dir(root_dir, root_namespace)
end
on_setup_callbacks.each(&:call)
@@ -126,6 +140,8 @@
# @sig () -> void
def unload
mutex.synchronize do
+ raise SetupRequired unless @setup
+
# We are going to keep track of the files that were required by our
# autoloads to later remove them from $LOADED_FEATURES, thus making
them
# loadable by Kernel#require again.
@@ -181,10 +197,11 @@
autoloads.clear
autoloaded_dirs.clear
to_unload.clear
- lazy_subdirs.clear
+ namespace_dirs.clear
+ shadowed_files.clear
Registry.on_unload(self)
- ExplicitNamespace.unregister_loader(self)
+ ExplicitNamespace.__unregister_loader(self)
@setup = false
@eager_loaded = false
@@ -201,6 +218,7 @@
# @sig () -> void
def reload
raise ReloadingDisabledError unless reloading_enabled?
+ raise SetupRequired unless @setup
unload
recompute_ignored_paths
@@ -208,58 +226,6 @@
setup
end
- # Eager loads all files in the root directories, recursively. Files do not
- # need to be in `$LOAD_PATH`, absolute file names are used. Ignored files
- # are not eager loaded. You can opt-out specifically in specific files and
- # directories with `do_not_eager_load`, and that can be overridden passing
- # `force: true`.
- #
- # @sig (true | false) -> void
- def eager_load(force: false)
- mutex.synchronize do
- break if @eager_loaded
-
- log("eager load start") if logger
-
- honour_exclusions = !force
-
- queue = []
- actual_root_dirs.each do |root_dir, namespace|
- queue << [namespace, root_dir] unless honour_exclusions &&
excluded_from_eager_load?(root_dir)
- end
-
- while to_eager_load = queue.shift
- namespace, dir = to_eager_load
-
- ls(dir) do |basename, abspath|
- next if honour_exclusions && excluded_from_eager_load?(abspath)
-
- if ruby?(abspath)
- if cref = autoloads[abspath]
- cget(*cref)
- end
- elsif !root_dirs.key?(abspath)
- if collapse?(abspath)
- queue << [namespace, abspath]
- else
- cname = inflector.camelize(basename, abspath)
- queue << [cget(namespace, cname), abspath]
- end
- end
- end
- end
-
- autoloaded_dirs.each do |autoloaded_dir|
- Registry.unregister_autoload(autoloaded_dir)
- end
- autoloaded_dirs.clear
-
- @eager_loaded = true
-
- log("eager load end") if logger
- end
- end
-
# Says if the given constant path would be unloaded on reload. This
# predicate returns `false` if reloading is disabled.
#
@@ -282,7 +248,16 @@
# @sig () -> void
def unregister
Registry.unregister_loader(self)
- ExplicitNamespace.unregister_loader(self)
+ ExplicitNamespace.__unregister_loader(self)
+ end
+
+ # The return value of this predicate is only meaningful if the loader has
+ # scanned the file. This is the case in the spots where we use it.
+ #
+ # @private
+ # @sig (String) -> Boolean
+ def shadowed_file?(file)
+ shadowed_files.member?(file)
end
# --- Class methods
---------------------------------------------------------------------------
@@ -311,11 +286,32 @@
Registry.loader_for_gem(called_from, warn_on_extra_files:
warn_on_extra_files)
end
- # Broadcasts `eager_load` to all loaders.
+ # Broadcasts `eager_load` to all loaders. Those that have not been setup
+ # are skipped.
#
# @sig () -> void
def eager_load_all
- Registry.loaders.each(&:eager_load)
+ Registry.loaders.each do |loader|
+ begin
+ loader.eager_load
+ rescue SetupRequired
+ # This is fine, we eager load what can be eager loaded.
+ end
+ end
+ end
+
+ # Broadcasts `eager_load_namespace` to all loaders. Those that have not
+ # been setup are skipped.
+ #
+ # @sig (Module) -> void
+ def eager_load_namespace(mod)
+ Registry.loaders.each do |loader|
+ begin
+ loader.eager_load_namespace(mod)
+ rescue SetupRequired
+ # This is fine, we eager load what can be eager loaded.
+ end
+ end
end
# Returns an array with the absolute paths of the root directories of all
@@ -338,19 +334,11 @@
cname = inflector.camelize(basename, abspath).to_sym
autoload_file(parent, cname, abspath)
else
- # In a Rails application, `app/models/concerns` is a subdirectory
of
- # `app/models`, but both of them are root directories.
- #
- # To resolve the ambiguity file name -> constant path this
introduces,
- # the `app/models/concerns` directory is totally ignored as a
namespace,
- # it counts only as root. The guard checks that.
- unless root_dir?(abspath)
+ if collapse?(abspath)
+ set_autoloads_in_dir(abspath, parent)
+ else
cname = inflector.camelize(basename, abspath).to_sym
- if collapse?(abspath)
- set_autoloads_in_dir(abspath, parent)
- else
- autoload_subdir(parent, cname, abspath)
- end
+ autoload_subdir(parent, cname, abspath)
end
end
rescue ::NameError => error
@@ -380,10 +368,10 @@
# We do not need to issue another autoload, the existing one is enough
# no matter if it is for a file or a directory. Just remember the
# subdirectory has to be visited if the namespace is used.
- lazy_subdirs[cpath] << subdir
+ namespace_dirs[cpath] << subdir
elsif !cdef?(parent, cname)
# First time we find this namespace, set an autoload for it.
- lazy_subdirs[cpath(parent, cname)] << subdir
+ namespace_dirs[cpath(parent, cname)] << subdir
set_autoload(parent, cname, subdir)
else
# For whatever reason the constant that corresponds to this namespace
has
@@ -398,6 +386,7 @@
if autoload_path = strict_autoload_path(parent, cname) ||
Registry.inception?(cpath(parent, cname))
# First autoload for a Ruby file wins, just ignore subsequent ones.
if ruby?(autoload_path)
+ shadowed_files << file
log("file #{file} is ignored because #{autoload_path} has
precedence") if logger
else
promote_namespace_from_implicit_to_explicit(
@@ -408,6 +397,7 @@
)
end
elsif cdef?(parent, cname)
+ shadowed_files << file
log("file #{file} is ignored because #{cpath(parent, cname)} is
already defined") if logger
else
set_autoload(parent, cname, file)
@@ -460,25 +450,26 @@
# @sig (String) -> void
def register_explicit_namespace(cpath)
- ExplicitNamespace.register(cpath, self)
+ ExplicitNamespace.__register(cpath, self)
end
# @sig (String) -> void
def raise_if_conflicting_directory(dir)
MUTEX.synchronize do
+ dir_slash = dir + "/"
+
Registry.loaders.each do |loader|
next if loader == self
- next if loader.ignores?(dir)
+ next if loader.__ignores?(dir)
- dir = dir + "/"
- loader.root_dirs.each do |root_dir, _namespace|
+ loader.__roots.each_key do |root_dir|
next if ignores?(root_dir)
- root_dir = root_dir + "/"
- if dir.start_with?(root_dir) || root_dir.start_with?(dir)
+ root_dir_slash = root_dir + "/"
+ if dir_slash.start_with?(root_dir_slash) ||
root_dir_slash.start_with?(dir_slash)
require "pp" # Needed for pretty_inspect, even in Ruby 2.5.
raise Error,
- "loader\n\n#{pretty_inspect}\n\nwants to manage directory
#{dir.chop}," \
+ "loader\n\n#{pretty_inspect}\n\nwants to manage directory
#{dir}," \
" which is already managed by\n\n#{loader.pretty_inspect}\n"
EOS
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk/version.rb new/lib/zeitwerk/version.rb
--- old/lib/zeitwerk/version.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk/version.rb 2022-11-08 09:02:43.000000000 +0100
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Zeitwerk
- VERSION = "2.6.1"
+ VERSION = "2.6.6"
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/zeitwerk.rb new/lib/zeitwerk.rb
--- old/lib/zeitwerk.rb 2022-10-01 00:18:05.000000000 +0200
+++ new/lib/zeitwerk.rb 2022-11-08 09:02:43.000000000 +0100
@@ -2,6 +2,7 @@
module Zeitwerk
require_relative "zeitwerk/real_mod_name"
+ require_relative "zeitwerk/internal"
require_relative "zeitwerk/loader"
require_relative "zeitwerk/gem_loader"
require_relative "zeitwerk/registry"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2022-10-01 00:18:05.000000000 +0200
+++ new/metadata 2022-11-08 09:02:43.000000000 +0100
@@ -1,14 +1,14 @@
--- !ruby/object:Gem::Specification
name: zeitwerk
version: !ruby/object:Gem::Version
- version: 2.6.1
+ version: 2.6.6
platform: ruby
authors:
- Xavier Noria
autorequire:
bindir: bin
cert_chain: []
-date: 2022-09-30 00:00:00.000000000 Z
+date: 2022-11-08 00:00:00.000000000 Z
dependencies: []
description: |2
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
@@ -28,10 +28,12 @@
- lib/zeitwerk/gem_inflector.rb
- lib/zeitwerk/gem_loader.rb
- lib/zeitwerk/inflector.rb
+- lib/zeitwerk/internal.rb
- lib/zeitwerk/kernel.rb
- lib/zeitwerk/loader.rb
- lib/zeitwerk/loader/callbacks.rb
- lib/zeitwerk/loader/config.rb
+- lib/zeitwerk/loader/eager_load.rb
- lib/zeitwerk/loader/helpers.rb
- lib/zeitwerk/real_mod_name.rb
- lib/zeitwerk/registry.rb