dry-rb / dry-operation

MIT License
24 stars 5 forks source link

Add Zeitwork custom loader #3

Closed bkuhlmann closed 1 year ago

bkuhlmann commented 1 year ago

Overview

Add a Zeitwerk custom loader as found in the Dry Monads implementation. This is based on this conversation.

Steps to Recreate

Notes from Tim:

I can go and inspect its setup [via]:

irb(main):002:0> Dry::System.loader
=>
#<Zeitwerk::Loader:0x000000010650d988
 @autoloaded_dirs=[],
 @autoloads=
  {"/Users/tim/Source/dry-rb/dry-system/lib/dry/system/auto_registrar.rb"=>[Dry::System, :AutoRegistrar],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/component.rb"=>[Dry::System, :Component],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/component_dir.rb"=>[Dry::System, :ComponentDir],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/config"=>[Dry::System, :Config],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/container.rb"=>[Dry::System, :Container],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/identifier.rb"=>[Dry::System, :Identifier],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/importer.rb"=>[Dry::System, :Importer],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/indirect_component.rb"=>[Dry::System, :IndirectComponent],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/loader.rb"=>[Dry::System, :Loader],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/magic_comments_parser.rb"=>[Dry::System, :MagicCommentsParser],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/manifest_registrar.rb"=>[Dry::System, :ManifestRegistrar],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/plugins.rb"=>[Dry::System, :Plugins],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/provider.rb"=>[Dry::System, :Provider],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/provider_registrar.rb"=>[Dry::System, :ProviderRegistrar],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/provider_source_registry.rb"=>[Dry::System, :ProviderSourceRegistry],
   "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/provider_sources.rb"=>[Dry::System, :ProviderSources]},
 @collapse_dirs=#<Set: {}>,
 @collapse_glob_patterns=#<Set: {}>,
 @eager_load_exclusions=#<Set: {}>,
 @eager_loaded=false,
 @ignored_glob_patterns=
  #<Set:
   {"/Users/tim/Source/dry-rb/dry-system/lib/dry-system.rb",
    "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/{components,constants,errors,stubs,version}.rb"}>,
 @ignored_paths=
  #<Set:
   {"/Users/tim/Source/dry-rb/dry-system/lib/dry-system.rb",
    "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/constants.rb",
    "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/errors.rb",
    "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/stubs.rb",
    "/Users/tim/Source/dry-rb/dry-system/lib/dry/system/version.rb"}>,
 @inflector=
  #<Zeitwerk::GemInflector:0x00000001063c1520
   @overrides={"source_dsl"=>"SourceDSL"},
   @version_file="/Users/tim/Source/dry-rb/dry-system/lib/dry-system/version.rb">,
 @initialized_at=2023-09-28 15:29:34.382951 +1000,
 @lazy_subdirs=
  {"Dry::System::Config"=>["/Users/tim/Source/dry-rb/dry-system/lib/dry/system/config"],
   "Dry::System::Loader"=>["/Users/tim/Source/dry-rb/dry-system/lib/dry/system/loader"],
   "Dry::System::Plugins"=>["/Users/tim/Source/dry-rb/dry-system/lib/dry/system/plugins"],
   "Dry::System::Provider"=>["/Users/tim/Source/dry-rb/dry-system/lib/dry/system/provider"],
   "Dry::System::ProviderSources"=>["/Users/tim/Source/dry-rb/dry-system/lib/dry/system/provider_sources"]},
 @logger=nil,
 @mutex=#<Thread::Mutex:0x00000001063c1778>,
 @mutex2=#<Thread::Mutex:0x00000001063c16d8>,
 @on_load_callbacks={},
 @on_setup_callbacks=[],
 @on_unload_callbacks={},
 @reloading_enabled=false,
 @root_dirs={"/Users/tim/Source/dry-rb/dry-system/lib"=>Object},
 @setup=true,
 @tag="dry-system",
 @to_unload={}>

You can of course go and find the loader inside Zeitwerk's Registry, but its .loaders is considered private and I know Xavier very much wants to keep it that way.

So in this way, it feels like a convenience with little downside, apart from, as you say, being slightly less "front and centre." But I think we do our best still by making it the very first method that appears inside the gem's main module.

In fact, looking at the output above, I think we actually need to change our setup code a bit. This is wrong:

 @inflector=
  #<Zeitwerk::GemInflector:0x00000001063c1520
   @overrides={"source_dsl"=>"SourceDSL"},
   @version_file="/Users/tim/Source/dry-rb/dry-system/lib/dry-system/version.rb">,

The @version_file should be dry/system/version.rb, not dry-system/version.rb. So I think we need to initialise the GemInflector like so: Zeitwerk::GemInflector.new("#{root}/dry/system.rb").

waiting-for-dev commented 1 year ago

Closed by #5