Skip to content

Releases: dry-rb/dry-system

v1.0.1

18 Nov 09:32
v1.0.1
5dfe7ae
Compare
Choose a tag to compare

Changed

  • Bumped dry-auto_inject dependency to its 1.0.0 final release (@solnic)

Compare v1.0.0...v1.0.1

v1.0.0

18 Nov 09:14
v1.0.0
Compare
Choose a tag to compare

Fixed

  • Only use DidYouMean-integrated Error for Component loading failure (via #261) (@cllns + @solnic)

Changed

Compare v0.27.2...v1.0.0

v1.0.0.rc1

04 Nov 18:04
v1.0.0.rc1
a89749b
Compare
Choose a tag to compare

Changed

Compare v0.27.2...v0.28.0

v0.27.2

17 Oct 05:19
v0.27.2
ec7b97f
Compare
Choose a tag to compare

Fixed

  • Removed remaining manual require left-overs (@solnic)

Compare v0.27.1...v0.27.2

v0.27.1

15 Oct 15:34
v0.27.1
Compare
Choose a tag to compare

Fixed

Compare v0.27.0...v0.27.1

v0.27.0

15 Oct 14:42
v0.27.0
b66ebfa
Compare
Choose a tag to compare

Changed

Compare v0.26.0...v0.27.0

v0.26.0

08 Oct 00:08
1f6f1a5
Compare
Choose a tag to compare

Changed

  • Update dry-configurable dependency to 0.16.0 and make internal adjustments to suit (@timriley in #249)
  • Remove now-unused concurrent-ruby gem dependency (@timriley in #250)

Compare v0.25.0...v0.26.0

v0.25.0

10 Jul 09:58
v0.25.0
Compare
Choose a tag to compare

Fixed

  • Fix incorrect type in ManifestRegistrar#finalize! (@alassek)

Changed

Compare v0.24.0...v0.25.0

v0.24.0

25 May 11:47
Compare
Choose a tag to compare

Changed

Compare v0.23.0...v0.24.0

v0.23.0

08 Feb 12:36
Compare
Choose a tag to compare

This is a major overhaul of bootable components (now known as “Providers”), and brings major advancements to other areas, including container imports and exports.

Deprecations are in place for otherwise breaking changes to commonly used parts of dry-system, though some breaking changes remain.

This prepares the way for dry-system 1.0, which will be released in the coming months.

Added

  • Containers can configure specific components for export using config.exports (@timriley in #209).

    class MyContainer < Dry::System::Container
      configure do |config|
        config.exports = %w[component_a component_b]
      end
    end

    Containers importing another container with configured exports will import only those components.

    When importing a specific set of components (see the note in the “Changed” section below), only those components whose keys intersect with the configured exports will be imported.

  • A :zeitwerk plugin, to set up Zeitwerk and integrate it with your container configuration (@ianks and @timriley in #197, #222, 13f8c87, #223)

    This makes it possible to enable Zeitwerk with a one-liner:

    class MyContainer < Dry::System::Container
      use :zeitwerk
    
      configure do |config|
        config.component_dirs.add "lib"
        # ...
      end
    end

    The plugin makes a Zeitwerk::Loader instance available at config.autoloader, and then in an after-:configure hook, the plugin will set up the loader to work with all of your configured component dirs and their namespaces. It will also enable the Dry::System::Loader::Autoloading loader for all component dirs, plus disable those dirs from being added to the $LOAD_PATH.

    The plugin accepts the following options:

    • loader: - (optional) to use a pre-initialized loader, if required.
    • run_setup: - (optional) a bool to determine whether to run Zeitwerk::Loader#setup as part of the after-:configure hook. This may be useful to disable in advanced cases when integrating with an externally managed loader.
    • eager_load: - (optional) a bool to determine whether to run Zeitwerk::Loader#eager_load as part of an after-:finalize hook. When not provided, it will default to true if the :env plugin is enabled and the env is set to :production.
    • debug: - (optional) a bool to set whether Zeitwerk should log to $stdout.
  • New Identifier#end_with? and Identifier#include? predicates (@timriley in #219)

    These are key segment-aware predicates that can be useful when checking components as part of container configuration.

    identifier.key # => "articles.operations.create"
    
    identifier.end_with?("create") # => true
    identifier.end_with?("operations.create") # => true
    identifier.end_with?("ate") # => false, not a whole segment
    identifier.end_with?("nope") # => false, not part of the key at all
    
    identifier.include?("operations") # => true
    identifier.include?("articles.operations") # => true
    identifier.include?("operations.create") # => true
    identifier.include?("article") # false, not a whole segment
    identifier.include?("update") # => false, not part of the key at all
  • An instance setting for component dirs allows simpler per-dir control over component instantiation (@timriley in #215)

    This optional setting should be provided a proc that receives a single Dry::System::Component instance as an argument, and should return the instance for the given component.

    configure do |config|
      config.component_dirs.add "lib" do |dir|
        dir.instance = proc do |component|
          if component.identifier.include?("workers")
            # Register classes for jobs
            component.loader.constant(component)
          else
            # Otherwise register regular instances per default loader
            component.loader.call(component)
          end
        end
      end
    end

    For complete control of component loading, you should continue to configure the component dir’s loader instead.

  • A new ComponentNotLoadableError error and helpful message is raised when resolving a component and an unexpected class is defined in the component’s source file (@cllns in #217).

    The error shows expected and found class names, and inflector configuration that may be required in the case of class names containing acronyms.

Fixed

  • Registrations made in providers (by calling register inside a provider step) have all their registration options preserved (such as a block-based registration, or the memoize: option) when having their registration merged into the target container after the provider lifecycle steps complete (@timriley in #212).

  • Providers can no longer implicitly re-start themselves while in the process of starting and cause an infinite loop (@timriley #213).

    This was possible before when a provider resolved a component from the target container that auto-injected dependencies with container keys sharing the same base key as the provider name.

Changed

  • “Bootable components” (also referred to in some places simply as “components”) have been renamed to “Providers” (@timriley in #200).

    Register a provider with Dry::System::Container.register_provider (Dry::System::Container.boot has been deprecated):

    MyContainer.register_provider(:mailer) do
      # ...
    end
  • Provider init lifecycle step has been deprecated and renamed to prepare (@timriley in #200).

    MyContainer.reigster_provider(:mailer) do
      # Rename `init` to `prepare`
      prepare do
        require "some/third_party/mailer"
      end
    end
  • Provider behavior is now backed by a class per provider, known as the “Provider source” (@timriley in #202).

    The provider source class is created for each provider as a subclass of Dry::System::Provider::Source.

    You can still register simple providers using the block-based DSL, but the class backing means you can share state between provider steps using regular instance variables:

    MyContainer.reigster_provider(:mailer) do
      prepare do
        require "some/third_party/mailer"
        @some_config = ThirdParty::Mailer::Config.new
      end
    
      start do
        # Since the `prepare` step will always run before start, we can access
        # @some_config here
        register "mailer", ThirdParty::Mailer.new(@some_config)
      end
    end

    Inside this register_provider block, self is the source subclass itself, and inside each of the step blocks (i.e. prepare do), self will be the instance of that provider source.

    For more complex providers, you can define your own source subclass and register it directly with the source: option for register_provider. This allows you to more readily use standard arrangements for factoring your logic within a class, such as extraction to another method:

    MyContainer.register_provider(:mailer, source: Class.new(Dry::System::Provider::Source) {
      # The provider lifecycle steps are ordinary methods
      def prepare
      end
    
      def start
        mailer = some_complex_logic_to_build_the_mailer(some: "config")
        register(:mailer, mailer)
      end
    
      private
    
      def some_complex_logic_to_build_the_mailer(**options)
        # ...
      end
    })
  • The block argument to Dry::System::Container.register_provider (previously .boot) has been deprecated. (@timriley in #202).

    This argument was used to give you access to the provider's target container (i.e. the container on which you were registering the provider).

    To access the target container, you can use #target_container (or #target as a convenience alias) instead.

    You can also access the provider's own container (which is where the provider's components are registered when you call register directly inside a provider step) as #provider_container (or #container as a convenience alias).

  • use(provider_name) inside a provider step has been deprecated. Use target_container.start(provider_name) instead (@timriley in #211 and #224)

    Now that you can access target_container consistently within all provider steps, you can use it to also start any other providers as you require without any special additional method. This also allows you to invoke other provider lifecycle steps, like target_container.prepare(provider_name).

  • method_missing-based delegation within providers to target container registrations has been removed (BREAKING) (@timriley in #202)

    Delegation to registrations with the provider's own container has been kept, since it can be a convenient way to access registrations made in a prior lifecycle step:

    MyContainer.register_provider(:mailer, namespace: true) do
      prepare do
        register :config, "mailer config here"
      end
    
      start do
        config # => "mailer config here"
      end
    end
  • The previous "external component" and "provider" concepts have been renamed to "external provider sources", in keeping with the new provider terminology outlined above (@timriley in #200 and #202).

    You can register a collection of external provider sources defined in their own source files via Dry::System.register_provider_sources (Dry::System.register_provider has been deprecated):

    require "dry/system"
    
    Dry::System.register_provider_sources(path)

    You can register an individual external provider source via Dry::System.register_provider_source (Dry::System.register_component has been deprecated):

    Dry::System.register_provider_source(:something, group: :my_gem) do
      start do
        # ...
      end
    end

    Just like providers, you can also register a class as an external provider source:

    module MyGem
      class MySource < Dry::System::Provider::Source
        def start
          # ...
        end
      end
    end
    
    Dry::System.register_provider_source(:somet...
Read more