Hello community,
here is the log from the commit of package rubygem-zeitwerk for
openSUSE:Factory checked in at 2020-07-14 08:00:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-zeitwerk (Old)
and /work/SRC/openSUSE:Factory/.rubygem-zeitwerk.new.3060 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-zeitwerk"
Tue Jul 14 08:00:55 2020 rev:5 rq:820704 version:2.3.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-zeitwerk/rubygem-zeitwerk.changes
2020-04-27 23:41:15.383860339 +0200
+++
/work/SRC/openSUSE:Factory/.rubygem-zeitwerk.new.3060/rubygem-zeitwerk.changes
2020-07-14 08:01:47.842276832 +0200
@@ -1,0 +2,11 @@
+Mon Jul 13 14:43:11 UTC 2020 - Manuel Schnitzer <[email protected]>
+
+- updated to version 2.3.1
+
+ * Saves some unnecessary allocations made internally by MRI. See
[#125](https://github.com/fxn/zeitwerk/pull/125), by
[@casperisfine](https://github.com/casperisfine).
+
+ * Documentation improvements.
+
+ * Internal code base maintenance.
+
+-------------------------------------------------------------------
Old:
----
zeitwerk-2.3.0.gem
New:
----
zeitwerk-2.3.1.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-zeitwerk.spec ++++++
--- /var/tmp/diff_new_pack.E5pWKe/_old 2020-07-14 08:01:50.014283865 +0200
+++ /var/tmp/diff_new_pack.E5pWKe/_new 2020-07-14 08:01:50.018283878 +0200
@@ -24,7 +24,7 @@
#
Name: rubygem-zeitwerk
-Version: 2.3.0
+Version: 2.3.1
Release: 0
%define mod_name zeitwerk
%define mod_full_name %{mod_name}-%{version}
@@ -42,7 +42,7 @@
%description
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
and application may have their own independent autoloader, with its own
-configuration, inflector, and logger. Supports autoloading, preloading,
+configuration, inflector, and logger. Supports autoloading,
reloading, and eager loading.
%prep
++++++ zeitwerk-2.3.0.gem -> zeitwerk-2.3.1.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md 2020-03-03 20:52:17.000000000 +0100
+++ new/README.md 2020-06-29 01:10:59.000000000 +0200
@@ -16,6 +16,8 @@
- [Nested root directories](#nested-root-directories)
- [Usage](#usage)
- [Setup](#setup)
+ - [Generic](#generic)
+ - [for_gem](#for_gem)
- [Autoloading](#autoloading)
- [Eager loading](#eager-loading)
- [Reloading](#reloading)
@@ -30,8 +32,11 @@
- [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)
- [Edge cases](#edge-cases)
+ - [Reopening third-party namespaces](#reopening-third-party-namespaces)
- [Rules of thumb](#rules-of-thumb)
- - [Autoloading, explicit namespaces, and
debuggers](#autoloading-explicit-namespaces-and-debuggers)
+ - [Debuggers](#debuggers)
+ - [Break](#break)
+ - [Byebug](#byebug)
- [Pronunciation](#pronunciation)
- [Supported Ruby versions](#supported-ruby-versions)
- [Testing](#testing)
@@ -52,7 +57,9 @@
The gem is designed so that any project, gem dependency, application, etc. can
have their own independent loader, coexisting in the same process, managing
their own project trees, and independent of each other. Each loader has its own
configuration, inflector, and optional logger.
-Internally, Zeitwerk issues `require` calls exclusively using absolute file
names, so there are no costly file system lookups in `$LOAD_PATH`. Technically,
the directories managed by Zeitwerk do not even need to be in `$LOAD_PATH`.
Furthermore, Zeitwerk does only one single scan of the project tree, and it
descends into subdirectories lazily, only if their namespaces are used.
+Internally, Zeitwerk issues `require` calls exclusively using absolute file
names, so there are no costly file system lookups in `$LOAD_PATH`. Technically,
the directories managed by Zeitwerk do not even need to be in `$LOAD_PATH`.
+
+Furthermore, Zeitwerk does at most one single scan of the project tree, and it
descends into subdirectories lazily, only if their namespaces are used.
<a id="markdown-synopsis" name="synopsis"></a>
## Synopsis
@@ -211,6 +218,9 @@
<a id="markdown-setup" name="setup"></a>
### Setup
+<a id="markdown-generic" name="generic"></a>
+#### Generic
+
Loaders are ready to load code right after calling `setup` on them:
```ruby
@@ -227,9 +237,36 @@
loader.setup
```
-The loader returned by `Zeitwerk::Loader.for_gem` has the directory of the
caller pushed, normally that is the absolute path of `lib`. In that sense,
`for_gem` can be used also by projects with a gem structure, even if they are
not technically gems. That is, you don't need a gemspec or anything.
+<a id="markdown-for_gem" name="for_gem"></a>
+#### for_gem
+
+`Zeitwerk::Loader.for_gem` is a convenience shortcut for the common case in
which a gem has its entry point directly under the `lib` directory:
+
+```
+lib/my_gem.rb # MyGem
+lib/my_gem/version.rb # MyGem::VERSION
+lib/my_gem/foo.rb # MyGem::Foo
+```
+
+Neither a gemspec nor a version file are technically required, this helper
works as long as the code is organized using that standard structure.
+
+If the entry point of your gem lives in a subdirectory of `lib` because it is
reopening a namespace defined somewhere else, please use the generic API to
setup the loader, and make sure you check the section [_Reopening third-party
namespaces_](https://github.com/fxn/zeitwerk#reopening-third-party-namespaces)
down below.
+
+Conceptually, `for_gem` translates to:
+
+```ruby
+# lib/my_gem.rb
+
+require "zeitwerk"
+loader = Zeitwerk::Loader.new
+loader.tag = File.basename(__FILE__, ".rb")
+loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
+loader.push_dir(__dir__)
+```
+
+except that this method returns the same object in subsequent calls from the
same file, in the unlikely case the gem wants to be able to reload.
-If the main module of a library references project constants at the top-level,
Zeitwerk has to be ready to load them. Their definitions, in turn, may
reference other project constants. And this is recursive. Therefore, it is
important that the `setup` call happens above the main module definition:
+If the main module references project constants at the top-level, Zeitwerk has
to be ready to load them. Their definitions, in turn, may reference other
project constants. And this is recursive. Therefore, it is important that the
`setup` call happens above the main module definition:
```ruby
# lib/my_gem.rb (main file)
@@ -245,8 +282,6 @@
end
```
-Zeitwerk works internally only with absolute paths to avoid costly file
searches in `$LOAD_PATH`. Indeed, the root directories do not even need to
belong to `$LOAD_PATH`, everything just works by design if they don't.
-
<a id="markdown-autoloading" name="autoloading"></a>
### Autoloading
@@ -619,6 +654,42 @@
This only affects explicit namespaces, those idioms work well for any other
ordinary class or module.
+<a id="markdown-reopening-third-party-namespaces"
name="reopening-third-party-namespaces"></a>
+### Reopening third-party namespaces
+
+Projects managed by Zeitwerk can work with namespaces defined by third-party
libraries. However, they have to be loaded in memory before calling `setup`.
+
+For example, let's imagine you're writing a gem that implements an adapter for
[Active Job](https://guides.rubyonrails.org/active_job_basics.html) that uses
AwesomeQueue as backend. By convention, your gem has to define a class called
`ActiveJob::QueueAdapters::AwesomeQueue`, and it has to do so in a file with a
matching path:
+
+```ruby
+# lib/active_job/queue_adapters/awesome_queue.rb
+module ActiveJob
+ module QueueAdapters
+ class AwesomeQueue
+ # ...
+ end
+ end
+end
+```
+
+It is very important that your gem _reopens_ the modules `ActiveJob` and
`ActiveJob::QueueAdapters` instead of _defining_ them. Because their proper
definition lives in Active Job. Furthermore, if the project reloads, you do not
want any of `ActiveJob` or `ActiveJob::QueueAdapters` to be reloaded.
+
+Bottom line, Zeitwerk should not be managing those namespaces. Active Job owns
them and defines them. Your gem needs to _reopen_ them.
+
+In order to do so, you need to make sure those modules are loaded before
calling `setup`. For instance, in the entry file for the gem:
+
+```ruby
+# Ensure these namespaces are reopened, not defined.
+require "active_job"
+require "active_job/queue_adapters"
+
+require "zeitwerk"
+loader = Zeitwerk::Loader.for_gem
+loader.setup
+```
+
+With that, when Zeitwerk scans the file system and reaches the gem directories
`lib/active_job` and `lib/active_job/queue_adapters`, it detects the
corresponding modules already exist and therefore understands it does not have
to manage them. The loader just descends into those directories. Eventually
will reach `lib/active_job/queue_adapters/awesome_queue.rb`, and since
`ActiveJob::QueueAdapters::AwesomeQueue` is unknown, Zeitwerk will manage it.
Which is what happens regularly with the files in your gem. On reload, the
namespaces are safe, won't be reloaded. The loader only reloads what it
manages, which in this case is the adapter itself.
+
<a id="markdown-rules-of-thumb" name="rules-of-thumb"></a>
### Rules of thumb
@@ -634,14 +705,18 @@
6. In a given process, ideally, there should be at most one loader with
reloading enabled. Technically, you can have more, but it may get tricky if one
refers to constants managed by the other one. Do that only if you know what you
are doing.
-<a id="markdown-autoloading-explicit-namespaces-and-debuggers"
name="autoloading-explicit-namespaces-and-debuggers"></a>
-### Autoloading, explicit namespaces, and debuggers
+<a id="markdown-debuggers" name="debuggers"></a>
+### Debuggers
+
+<a id="markdown-break" name="break"></a>
+#### Break
-As of this writing, Zeitwerk is unable to autoload classes or modules that
belong to [explicit namespaces](#explicit-namespaces) inside debugger sessions.
You'll get a `NameError`.
+Zeitwerk works fine with [@gsamokovarov](https://github.com/gsamokovarov)'s
[Break](https://github.com/gsamokovarov/break) debugger.
-The root cause is that debuggers set trace points, and Zeitwerk does too to
support explicit namespaces. A debugger session happens inside a trace point
handler, and Ruby does not invoke other handlers from within a running handler.
Therefore, the code that manages explicit namespaces in Zeitwerk does not get
called by the interpreter. See [this
issue](https://github.com/deivid-rodriguez/byebug/issues/564#issuecomment-499413606)
for further details.
+<a id="markdown-byebug" name="byebug"></a>
+#### Byebug
-As a workaround, you can eager load. Zeitwerk tries hard to succeed or fail
consistently both autoloading and eager loading, so switching to eager loading
should not introduce any interference in your debugging logic, generally
speaking.
+Zeitwerk and [Byebug](https://github.com/deivid-rodriguez/byebug) are
incompatible, classes or modules that belong to [explicit
namespaces](#explicit-namespaces) are not autoloaded inside a Byebug session.
See [this
issue](https://github.com/deivid-rodriguez/byebug/issues/564#issuecomment-499413606)
for further details.
<a id="markdown-pronunciation" name="pronunciation"></a>
## Pronunciation
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/kernel.rb new/lib/zeitwerk/kernel.rb
--- old/lib/zeitwerk/kernel.rb 2020-03-03 20:52:17.000000000 +0100
+++ new/lib/zeitwerk/kernel.rb 2020-06-29 01:10:59.000000000 +0200
@@ -3,6 +3,17 @@
module Kernel
module_function
+ # We are going to decorate Kerner#require with two goals.
+ #
+ # First, by intercepting Kernel#require calls, we are able to autovivify
+ # modules on required directories, and also do internal housekeeping when
+ # managed files are loaded.
+ #
+ # On the other hand, if you publish a new version of a gem that is now
managed
+ # by Zeitwerk, client code can reference directly your classes and modules
and
+ # should not require anything. But if someone has legacy require calls
around,
+ # they will work as expected, and in a compatible way.
+ #
# We cannot decorate with prepend + super because Kernel has already been
# included in Object, and changes in ancestors don't get propagated into
# already existing ancestor chains.
@@ -30,4 +41,24 @@
end
end
end
+
+ # By now, I have seen no way so far to decorate require_relative.
+ #
+ # For starters, at least in CRuby, require_relative does not delegate to
+ # require. Both require and require_relative delegate the bulk of their work
+ # to an internal C function called rb_require_safe. So, our require wrapper
is
+ # not executed.
+ #
+ # On the other hand, we cannot use the aliasing technique above because
+ # require_relative receives a path relative to the directory of the file in
+ # which the call is performed. If a wrapper here invoked the original method,
+ # Ruby would resolve the relative path taking lib/zeitwerk as base directory.
+ #
+ # A workaround could be to extract the base directory from caller_locations,
+ # but what if someone else decorated require_relative before us? You can't
+ # really know with certainty where's the original call site in the stack.
+ #
+ # However, the main use case for require_relative is to load files from your
+ # own project. Projects managed by Zeitwerk don't do this for files managed
by
+ # Zeitwerk, precisely.
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 2020-03-03 20:52:17.000000000 +0100
+++ new/lib/zeitwerk/loader.rb 2020-06-29 01:10:59.000000000 +0200
@@ -464,7 +464,7 @@
# require "zeitwerk"
# loader = Zeitwerk::Loader.new
# loader.tag = File.basename(__FILE__, ".rb")
- # loader.inflector = Zeitwerk::GemInflector.new
+ # loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
# loader.push_dir(__dir__)
#
# except that this method returns the same object in subsequent calls
from
@@ -616,7 +616,10 @@
# $LOADED_FEATURES stores real paths since Ruby 2.4.4. We set and save
the
# real path to be able to delete it from $LOADED_FEATURES on unload, and
to
# be able to do a lookup later in Kernel#require for manual require
calls.
- realpath = File.realpath(abspath)
+ #
+ # We freeze realpath because that saves allocations in Module#autoload.
+ # See #125.
+ realpath = File.realpath(abspath).freeze
parent.autoload(cname, realpath)
if logger
if ruby?(realpath)
@@ -719,8 +722,13 @@
def ls(dir)
Dir.foreach(dir) do |basename|
next if basename.start_with?(".")
+
abspath = File.join(dir, basename)
- yield basename, abspath unless ignored_paths.member?(abspath)
+ next if ignored_paths.member?(abspath)
+
+ # We freeze abspath because that saves allocations when passed later to
+ # File methods. See #125.
+ yield basename, abspath.freeze
end
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 2020-03-03 20:52:17.000000000 +0100
+++ new/lib/zeitwerk/version.rb 2020-06-29 01:10:59.000000000 +0200
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Zeitwerk
- VERSION = "2.3.0"
+ VERSION = "2.3.1"
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2020-03-03 20:52:17.000000000 +0100
+++ new/metadata 2020-06-29 01:10:59.000000000 +0200
@@ -1,19 +1,19 @@
--- !ruby/object:Gem::Specification
name: zeitwerk
version: !ruby/object:Gem::Version
- version: 2.3.0
+ version: 2.3.1
platform: ruby
authors:
- Xavier Noria
autorequire:
bindir: bin
cert_chain: []
-date: 2020-03-03 00:00:00.000000000 Z
+date: 2020-06-28 00:00:00.000000000 Z
dependencies: []
description: |2
Zeitwerk implements constant autoloading with Ruby semantics. Each gem
and application may have their own independent autoloader, with its own
- configuration, inflector, and logger. Supports autoloading, preloading,
+ configuration, inflector, and logger. Supports autoloading,
reloading, and eager loading.
email: [email protected]
executables: []
@@ -36,7 +36,11 @@
homepage: https://github.com/fxn/zeitwerk
licenses:
- MIT
-metadata: {}
+metadata:
+ homepage_uri: https://github.com/fxn/zeitwerk
+ changelog_uri: https://github.com/fxn/zeitwerk/blob/master/CHANGELOG.md
+ source_code_uri: https://github.com/fxn/zeitwerk
+ bug_tracker_uri: https://github.com/fxn/zeitwerk/issues
post_install_message:
rdoc_options: []
require_paths:
@@ -52,7 +56,7 @@
- !ruby/object:Gem::Version
version: '0'
requirements: []
-rubygems_version: 3.0.3
+rubygems_version: 3.1.2
signing_key:
specification_version: 4
summary: Efficient and thread-safe constant autoloader