Module: Dry::Core::Container::Mixin

Included in:
Dry::Core::Container
Defined in:
lib/dry/core/container/mixin.rb,
lib/dry/core/container/stub.rb

Overview

Mixin to expose Inversion of Control (IoC) container behaviour

rubocop:disable Metrics/ModuleLength

Examples:


class MyClass
  extend Dry::Core::Container::Mixin
end

MyClass.register(:item, 'item')
MyClass.resolve(:item)
=> 'item'

class MyObject
  include Dry::Core::Container::Mixin
end

container = MyObject.new
container.register(:item, 'item')
container.resolve(:item)
=> 'item'

Defined Under Namespace

Modules: Initializer

Constant Summary collapse

PREFIX_NAMESPACE =
lambda do |namespace, key, config|
  [namespace, key].join(config.namespace_separator)
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(base) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/dry/core/container/mixin.rb', line 53

def self.extended(base)
  hooks_mod = ::Module.new do
    def inherited(subclass)
      subclass.instance_variable_set(:@_container, @_container.dup)
      super
    end
  end

  base.class_eval do
    extend Configuration
    extend hooks_mod

    @_container = ::Concurrent::Hash.new
  end
end

.included(base) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/dry/core/container/mixin.rb', line 78

def self.included(base)
  base.class_eval do
    extend Configuration
    prepend Initializer

    def config
      self.class.config
    end
  end
end

Instance Method Details

#[](key) ⇒ Mixed

Resolve an item from the container

Parameters:

  • key (Mixed)

    The key for the item you wish to resolve

Returns:

  • (Mixed)

See Also:



144
145
146
# File 'lib/dry/core/container/mixin.rb', line 144

def [](key)
  resolve(key)
end

#_containerObject



288
289
290
# File 'lib/dry/core/container/mixin.rb', line 288

def _container
  @_container
end

#cloneObject



300
301
302
303
304
305
306
# File 'lib/dry/core/container/mixin.rb', line 300

def clone
  copy = super
  unless copy.frozen?
    copy.instance_variable_set(:@_container, _container.dup)
  end
  copy
end

#decorate(key, with: nil, &block) ⇒ Dry::Core::Container::Mixin

Decorates an item from the container with specified decorator

Returns:



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/dry/core/container/mixin.rb', line 227

def decorate(key, with: nil, &block)
  key = key.to_s
  original = _container.delete(key) do
    raise KeyError, "Nothing registered with the key #{key.inspect}"
  end

  if with.is_a?(Class)
    decorator = with.method(:new)
  elsif block.nil? && !with.respond_to?(:call)
    raise Error, "Decorator needs to be a Class, block, or respond to the `call` method"
  else
    decorator = with || block
  end

  _container[key] = original.map(decorator)
  self
end

#dupObject



293
294
295
296
297
# File 'lib/dry/core/container/mixin.rb', line 293

def dup
  copy = super
  copy.instance_variable_set(:@_container, _container.dup)
  copy
end

#each(&block) ⇒ Enumerator

Note:

In discussions with other developers, it was felt that being able to iterate over not just the registered keys, but to see what was registered would be very helpful. This is a step toward doing that.

Calls block once for each key/value pair in the container, passing the key and the registered item parameters.

If no block is given, an enumerator is returned instead.

Returns:

  • (Enumerator)


218
219
220
# File 'lib/dry/core/container/mixin.rb', line 218

def each(&block)
  config.resolver.each(_container, &block)
end

#each_key(&block) ⇒ Dry::Core::Container::Mixin

Calls block once for each key in container, passing the key as a parameter.

If no block is given, an enumerator is returned instead.

Returns:



201
202
203
204
# File 'lib/dry/core/container/mixin.rb', line 201

def each_key(&block)
  config.resolver.each_key(_container, &block)
  self
end

#enable_stubs!Object

Enable stubbing functionality into the current container



51
52
53
# File 'lib/dry/core/container/stub.rb', line 51

def enable_stubs!
  extend ::Dry::Core::Container::Stub
end

#freezeObject

Freeze the container. Nothing can be registered after freezing



281
282
283
284
285
# File 'lib/dry/core/container/mixin.rb', line 281

def freeze
  super
  _container.freeze
  self
end

#import(namespace) ⇒ Dry::Core::Container::Mixin

Import a namespace

Parameters:

Returns:



272
273
274
275
276
# File 'lib/dry/core/container/mixin.rb', line 272

def import(namespace)
  namespace(namespace.name, &namespace.block)

  self
end

#key?(key) ⇒ Bool

Check whether an item is registered under the given key

Parameters:

  • key (Mixed)

    The key you wish to check for registration with

Returns:

  • (Bool)


181
182
183
# File 'lib/dry/core/container/mixin.rb', line 181

def key?(key)
  config.resolver.key?(_container, key)
end

#keysArray<String>

An array of registered names for the container

Returns:

  • (Array<String>)


190
191
192
# File 'lib/dry/core/container/mixin.rb', line 190

def keys
  config.resolver.keys(_container)
end

#merge(other, namespace: nil, &block) ⇒ Dry::Core::Container::Mixin

Merge in the items of the other container

Parameters:

  • other (Dry::Core::Container)

    The other container to merge in

  • namespace (Symbol, nil) (defaults to: nil)

    Namespace to prefix other container items with, defaults to nil

Returns:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/dry/core/container/mixin.rb', line 158

def merge(other, namespace: nil, &block)
  if namespace
    _container.merge!(
      other._container.each_with_object(::Concurrent::Hash.new) { |(key, item), hsh|
        hsh[PREFIX_NAMESPACE.call(namespace, key, config)] = item
      },
      &block
    )
  else
    _container.merge!(other._container, &block)
  end

  self
end

#namespace(namespace, &block) ⇒ Dry::Core::Container::Mixin

Evaluate block and register items in namespace

Parameters:

  • namespace (Mixed)

    The namespace to register items in

Returns:



253
254
255
256
257
258
259
260
261
262
# File 'lib/dry/core/container/mixin.rb', line 253

def namespace(namespace, &block)
  ::Dry::Core::Container::NamespaceDSL.new(
    self,
    namespace,
    config.namespace_separator,
    &block
  )

  self
end

#register(key, contents = nil, options = EMPTY_HASH) { ... } ⇒ Dry::Core::Container::Mixin

Register an item with the container to be resolved later

Parameters:

  • key (Mixed)

    The key to register the container item with (used to resolve)

  • contents (Mixed) (defaults to: nil)

    The item to register with the container (if no block given)

  • options (Hash) (defaults to: EMPTY_HASH)

    Options to pass to the registry when registering the item

Yields:

  • If a block is given, contents will be ignored and the block will be registered instead

Returns:



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/dry/core/container/mixin.rb', line 104

def register(key, contents = nil, options = EMPTY_HASH, &block)
  if block_given?
    item = block
    options = contents if contents.is_a?(::Hash)
  else
    item = contents
  end

  config.registry.call(_container, key, item, options)

  self
rescue FrozenError
  raise FrozenError,
        "can't modify frozen #{self.class} (when attempting to register '#{key}')"
end

#resolve(key) {|key| ... } ⇒ Mixed

Resolve an item from the container

Parameters:

  • key (Mixed)

    The key for the item you wish to resolve

Yields:

  • Fallback block to call when a key is missing. Its result will be returned

Yield Parameters:

  • key (Mixed)

    Missing key

Returns:

  • (Mixed)


131
132
133
# File 'lib/dry/core/container/mixin.rb', line 131

def resolve(key, &block)
  config.resolver.call(_container, key, &block)
end