dry-container

repository·main·Indexed 43 days ago

https://github.com/dry-rb/dry-container

A dependency injection container for Ruby that provides a centralized way to manage and register objects for decoupled and testable architectures. It supports Inversion of Control (IoC), namespaces, and stubbing for tests. Note: dry-container is deprecated; functionality has been moved to Dry::Core::Container within the dry-core gem.

Tokens
3.6K
Snippets
18
Records
20
Agent score
56%

What's inside dry-container

  1. How stubbing affects key resolution

    main
    When stubbing is enabled, the resolve method is overridden to prioritize stubs. When you call resolve(key), the container first checks if a stub exists for that key. If a stub is found, it returns the stubbed value; otherwise, it falls back to the standard container resolution logic (the original value).
  2. Enable stubbing functionality in a container

    main

    To use the stubbing API for testing, you must first enable it in your container instance. This is done by calling enable_stubs! on the container object. This method extends the container with the Dry::Container::Stub module, allowing you to temporarily replace container items with stubs during tests.

    container.enable_stubs!
  3. Add container functionality to a class using Dry::Container::Mixin

    main

    You can turn any class or object into an Inversion of Control (IoC) container by using Dry::Container::Mixin.

    • Extending a class: If you extend Dry::Container::Mixin, the class itself becomes the container (singleton behavior). Methods like register and resolve are called on the class.
    • Including in a class: If you include Dry::Container::Mixin, instances of that class become containers. Each instance maintains its own separate container state.
    # Class-level container
    class MyClass
      extend Dry::Container::Mixin
    end
    
    MyClass.register(:item, 'item')
    MyClass.resolve(:item)
    # => 'item'
    
    # Instance-level container
    class MyObject
      include Dry::Container::Mixin
    end
    
    container = MyObject.new
    container.register(:item, 'item')
    container.resolve(:item)
    # => 'item'
  4. Migration notice: dry-container is deprecated

    main

    The dry-container gem is deprecated. The container functionality has been moved into dry-core.

    • If you already have dry-core installed and it defines Dry::Core::Container, you should use Dry::Core::Container instead.
    • Otherwise, you should upgrade to the latest version of dry-core to access the built-in Dry::Core::Container.
  5. Handle container lookup errors

    main

    When performing lookups in a Dry::Container, errors related to missing keys are raised as Dry::Container::KeyError. This class inherits from the standard Ruby ::KeyError and is designed to work with Ruby's DidYouMean feature to suggest potential key corrections if the did_you_mean gem is available.

    begin
      container.resolve(:missing_key)
    rescue Dry::Container::KeyError => e
      # Handle the missing key error
    end
  6. Remove stubbed keys from the container

    main

    Use unstub to remove stubs and restore the original container values.

    • To remove specific keys: unstub(:key1, :key2)
    • To remove all currently active stubs: unstub (with no arguments)
    # Remove specific keys
    container.unstub(:my_key)
    
    # Remove all active stubs
    container.unstub
  7. Resolve items from a container

    main

    Retrieve registered items using resolve(key) or the bracket syntax container[key].

    • key: The identifier of the item.
    • &block: An optional fallback block that is called if the key is missing. The result of this block is returned.
    # Basic resolution
    container.resolve(:item)
    container[:item]
    
    # Resolution with a fallback block
    container.resolve(:missing_key) { 'fallback' }
    # => 'fallback'
  8. Create and import a Namespace to organize container keys

    main

    A Dry::Container::Namespace allows you to group related registrations under a specific prefix. You define a namespace by providing a name and a block containing your registrations. When you import this namespace into a Dry::Container, all keys defined within the block are automatically prefixed with the namespace name using dot notation (e.g., namespace_name.key).

    ns = Dry::Container::Namespace.new('name') do
      register('item', 'item')
    end
    
    container = Dry::Container.new
    container.import(ns)
    
    container.resolve('name.item')
    # => 'item'
  9. Use Dry::Container for Inversion of Control (IoC)

    main

    A Dry::Container acts as an Inversion of Control (IoC) container, allowing you to register dependencies and resolve them later. You can register static values or procs/lambdas. By default, when you resolve a proc, the container calls it and returns the result. If you want to register the proc itself without executing it, use the call: false option.

    container = Dry::Container.new
    
    # Register a static value
    container.register(:item, 'item')
    container.resolve(:item)
    # => 'item'
    
    # Register a proc (executes on resolution by default)
    container.register(:item1, -> { 'item' })
    container.resolve(:item1)
    # => 'item'
    
    # Register a proc without executing it
    container.register(:item2, -> { 'item' }, call: false)
    container.resolve(:item2)
    # => #<Proc:0x...>
  10. Register items in a container

    main

    Use the register method to add items to the container. You can register a static value or a block (which will be executed when the item is resolved).

    • key: The identifier used to resolve the item.
    • contents: The value to register. If a block is provided, this is ignored.
    • options: A hash of options passed to the registry.
    • &block: If provided, the block is registered instead of the contents value.
    # Registering a value
    container.register(:item, 'item')
    
    # Registering a block (lazy registration)
    container.register(:item) { 'item' }
    
    # Registering with options
    container.register(:item, 'item', some_option: true)
  11. Stub a key in the container

    main

    The stub method allows you to replace the value of an existing key with a temporary value.

    Requirements:

    • The key must already exist in the container. If the key is missing, an ArgumentError is raised.

    Usage Patterns:

    1. Permanent Stub (for the duration of the test session): Call stub(key, value) to replace the key. You must manually call unstub(key) later to restore the original value.
    2. Temporary Block-based Stub: Pass a block to stub(key, value) { ... }. The stub will be automatically removed (unstubbed) once the block finishes executing. This is the recommended pattern for ensuring test isolation.
    # Manual stubbing
    container.stub(:my_key, stubbed_value)
    # ... do work ...
    container.unstub(:my_key)
    
    # Block-based stubbing (automatically unstubs after block)
    container.stub(:my_key, stubbed_value) do
      # code using the stubbed value
    end