Hello community, here is the log from the commit of package rubygem-zeitwerk for openSUSE:Factory checked in at 2020-12-11 20:16:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-zeitwerk (Old) and /work/SRC/openSUSE:Factory/.rubygem-zeitwerk.new.2328 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-zeitwerk" Fri Dec 11 20:16:25 2020 rev:8 rq:854709 version:2.4.2 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-zeitwerk/rubygem-zeitwerk.changes 2020-11-02 10:36:31.267301969 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-zeitwerk.new.2328/rubygem-zeitwerk.changes 2020-12-11 20:16:27.096653077 +0100 @@ -1,0 +2,20 @@ +Fri Dec 11 03:23:04 UTC 2020 - Manuel Schnitzer <mschnit...@suse.com> + +- updated to version 2.4.2 + + * Implements `Zeitwerk::Loader#on_load`, which allows you to configure blocks of code to be executed after a certain class or module have been loaded: + + ```ruby + # config/environments/development.rb + loader.on_load("SomeApiClient") do + SomeApiClient.endpoint = "https://api.dev" + + # config/environments/production.rb + loader.on_load("SomeApiClient") do + SomeApiClient.endpoint = "https://api.prod" + end + ``` + + See the [documentation](https://github.com/fxn/zeitwerk/blob/master/README.md#the-on_load-callback) for further details. + +------------------------------------------------------------------- Old: ---- zeitwerk-2.4.1.gem New: ---- zeitwerk-2.4.2.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-zeitwerk.spec ++++++ --- /var/tmp/diff_new_pack.nLNyNq/_old 2020-12-11 20:16:27.960653435 +0100 +++ /var/tmp/diff_new_pack.nLNyNq/_new 2020-12-11 20:16:27.964653436 +0100 @@ -24,7 +24,7 @@ # Name: rubygem-zeitwerk -Version: 2.4.1 +Version: 2.4.2 Release: 0 %define mod_name zeitwerk %define mod_full_name %{mod_name}-%{version} ++++++ zeitwerk-2.4.1.gem -> zeitwerk-2.4.2.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2020-10-29 11:34:25.000000000 +0100 +++ new/README.md 2020-11-27 15:15:47.000000000 +0100 @@ -25,6 +25,7 @@ - [Zeitwerk::Inflector](#zeitwerkinflector) - [Zeitwerk::GemInflector](#zeitwerkgeminflector) - [Custom inflector](#custom-inflector) + - [The on_load callback](#the-on_load-callback) - [Logging](#logging) - [Loader tag](#loader-tag) - [Ignoring parts of the project](#ignoring-parts-of-the-project) @@ -502,6 +503,52 @@ end ``` +<a id="markdown-the-on_load-callback" name="the-on_load-callback"></a> +### The on_load callback + +The usual place to run something when a file is loaded is the file itself. However, sometimes you'd like to be called, and this is possible with the `on_load` callback. + +For example, let's imagine this class belongs to a Rails application: + +```ruby +class SomeApiClient + class << self + attr_accessor :endpoint + end +end +``` + +With `on_load`, it is easy to schedule code at boot time that initializes `endpoint` according to the configuration: + +```ruby +# config/environments/development.rb +loader.on_load("SomeApiClient") do + SomeApiClient.endpoint = "https://api.dev" +end + +# config/environments/production.rb +loader.on_load("SomeApiClient") do + SomeApiClient.endpoint = "https://api.prod" +end +``` + +Uses cases: + +* Doing something with an autoloadable class or module in a Rails application during initialization, in a way that plays well with reloading. As in the previous example. +* Delaying the execution of the block until the class is loaded for performance. +* Delaying the execution of the block until the class is loaded because it follows the adapter pattern and better not to load the class if the user does not need it. +* Etc. + +However, let me stress that the easiest way to accomplish that is to write whatever you have to do in the actual target file. `on_load` use cases are edgy, use it only if appropriate. + +`on_load` receives the name of the target class or module as a string. The given block is executed every time its corresponding file is loaded. That includes reloads. + +Multiple callbacks on the same target are supported, and they run in order of definition. + +The block is executed once the loader has loaded the target. In particular, if the target was already loaded when the callback is defined, the block won't run. But if you reload and load the target again, then it will. Normally, you'll want to define `on_load` callbacks before `setup`. + +Defining a callback for a target not managed by the receiver is not an error, the block simply won't ever be executed. + <a id="markdown-logging" name="logging"></a> ### Logging 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-10-29 11:34:25.000000000 +0100 +++ new/lib/zeitwerk/kernel.rb 2020-11-27 15:15:47.000000000 +0100 @@ -28,6 +28,7 @@ end else loader.on_dir_autoloaded(path) + true end else zeitwerk_original_require(path).tap do |required| 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 2020-10-29 11:34:25.000000000 +0100 +++ new/lib/zeitwerk/loader/callbacks.rb 2020-11-27 15:15:47.000000000 +0100 @@ -6,15 +6,19 @@ # @private # @sig (String) -> void def on_file_autoloaded(file) - cref = autoloads.delete(file) - to_unload[cpath(*cref)] = [file, cref] if reloading_enabled? + cref = autoloads.delete(file) + cpath = cpath(*cref) + + to_unload[cpath] = [file, cref] if reloading_enabled? Zeitwerk::Registry.unregister_autoload(file) if logger && cdef?(*cref) - log("constant #{cpath(*cref)} loaded from file #{file}") + log("constant #{cpath} loaded from file #{file}") elsif !cdef?(*cref) - raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath(*cref)}, but didn't", cref.last) + raise Zeitwerk::NameError.new("expected file #{file} to define constant #{cpath}, but didn't", cref.last) end + + run_on_load_callbacks(cpath) end # Invoked from our decorated Kernel#require when a managed directory is @@ -37,9 +41,10 @@ mutex2.synchronize do if cref = autoloads.delete(dir) autovivified_module = cref[0].const_set(cref[1], Module.new) - log("module #{autovivified_module.name} autovivified from directory #{dir}") if logger + cpath = autovivified_module.name + log("module #{cpath} autovivified from directory #{dir}") if logger - to_unload[autovivified_module.name] = [dir, cref] if reloading_enabled? + to_unload[cpath] = [dir, cref] if reloading_enabled? # We don't unregister `dir` in the registry because concurrent threads # wouldn't find a loader associated to it in Kernel#require and would @@ -48,6 +53,8 @@ autoloaded_dirs << dir on_namespace_loaded(autovivified_module) + + run_on_load_callbacks(cpath) end end end @@ -65,4 +72,15 @@ end end end + + private + + # @sig (String) -> void + def run_on_load_callbacks(cpath) + # Very common, do not even compute a hash code. + return if on_load_callbacks.empty? + + callbacks = reloading_enabled? ? on_load_callbacks[cpath] : on_load_callbacks.delete(cpath) + callbacks.each(&:call) if callbacks + 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 2020-10-29 11:34:25.000000000 +0100 +++ new/lib/zeitwerk/loader.rb 2020-11-27 15:15:47.000000000 +0100 @@ -129,6 +129,9 @@ # @sig Set[String] attr_reader :eager_load_exclusions + # User-oriented callbacks to be fired when a constant is loaded. + attr_reader :on_load_callbacks + # @private # @sig Mutex attr_reader :mutex @@ -155,6 +158,7 @@ @to_unload = {} @lazy_subdirs = {} @eager_load_exclusions = Set.new + @on_load_callbacks = {} # TODO: find a better name for these mutexes. @mutex = Mutex.new @@ -262,6 +266,24 @@ end end + # Configure a block to be invoked once a certain constant path is loaded. + # Supports multiple callbacks, and if there are many, they are executed in + # the order in which they were defined. + # + # loader.on_load("SomeApiClient") do + # SomeApiClient.endpoint = "https://api.dev" + # end + # + # @raise [TypeError] + # @sig (String) { () -> void } -> void + def on_load(cpath, &block) + raise TypeError, "on_load only accepts strings" unless cpath.is_a?(String) + + mutex.synchronize do + (on_load_callbacks[cpath] ||= []) << block + end + end + # Sets autoloads in the root namespace and preloads files, if any. # # @sig () -> void 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-10-29 11:34:25.000000000 +0100 +++ new/lib/zeitwerk/version.rb 2020-11-27 15:15:47.000000000 +0100 @@ -1,5 +1,5 @@ # frozen_string_literal: true module Zeitwerk - VERSION = "2.4.1" + VERSION = "2.4.2" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2020-10-29 11:34:25.000000000 +0100 +++ new/metadata 2020-11-27 15:15:47.000000000 +0100 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: zeitwerk version: !ruby/object:Gem::Version - version: 2.4.1 + version: 2.4.2 platform: ruby authors: - Xavier Noria autorequire: bindir: bin cert_chain: [] -date: 2020-10-29 00:00:00.000000000 Z +date: 2020-11-27 00:00:00.000000000 Z dependencies: [] description: |2 Zeitwerk implements constant autoloading with Ruby semantics. Each gem _______________________________________________ openSUSE Commits mailing list -- commit@lists.opensuse.org To unsubscribe, email commit-le...@lists.opensuse.org List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette List Archives: https://lists.opensuse.org/archives/list/commit@lists.opensuse.org