# frozen_string_literal: truerequire"concurrent/hash"require"dry/core/constants"moduleDrymoduleCoreclassContainerinclude::Dry::Core::Constants# @api publicError=::Class.new(::StandardError)# Error raised when key is not defined in the registry## @api publicKeyError=::Class.new(::KeyError)ifdefined?(::DidYouMean::KeyErrorChecker)::DidYouMean.correct_error(KeyError,::DidYouMean::KeyErrorChecker)end# Mixin to expose Inversion of Control (IoC) container behaviour## @example## 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'## @api public## rubocop:disable Metrics/ModuleLengthmoduleMixinPREFIX_NAMESPACE=lambdado|namespace,key,config|[namespace,key].join(config.namespace_separator)end# @privatedefself.extended(base)hooks_mod=::Module.newdodefinherited(subclass)subclass.instance_variable_set(:@_container,@_container.dup)superendendbase.class_evaldoextendConfigurationextendhooks_mod@_container=::Concurrent::Hash.newendend# @privatemoduleInitializerdefinitialize(...)@_container=::Concurrent::Hash.newsuperendend# @privatedefself.included(base)base.class_evaldoextendConfigurationprependInitializerdefconfigself.class.configendendend# Register an item with the container to be resolved later## @param [Mixed] key# The key to register the container item with (used to resolve)# @param [Mixed] contents# The item to register with the container (if no block given)# @param [Hash] options# Options to pass to the registry when registering the item# @yield# If a block is given, contents will be ignored and the block# will be registered instead## @return [Dry::Core::Container::Mixin] self## @api publicdefregister(key,contents=nil,options=EMPTY_HASH,&block)ifblock_given?item=blockoptions=contentsifcontents.is_a?(::Hash)elseitem=contentsendconfig.registry.call(_container,key,item,options)selfrescue::FrozenErrorraise::FrozenError,"can't modify frozen #{self.class} (when attempting to register '#{key}')"end# Resolve an item from the container## @param [Mixed] key# The key for the item you wish to resolve# @yield# Fallback block to call when a key is missing. Its result will be returned# @yieldparam [Mixed] key Missing key## @return [Mixed]## @api publicdefresolve(key,&)config.resolver.call(_container,key,&)end# Resolve an item from the container## @param [Mixed] key# The key for the item you wish to resolve## @return [Mixed]## @api public# @see Dry::Core::Container::Mixin#resolvedef[](key)resolve(key)end# Merge in the items of the other container## @param [Dry::Core::Container] other# The other container to merge in# @param [Symbol, nil] namespace# Namespace to prefix other container items with, defaults to nil## @return [Dry::Core::Container::Mixin] self## @api publicdefmerge(other,namespace: nil,&block)ifnamespace_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)endselfend# Check whether an item is registered under the given key## @param [Mixed] key# The key you wish to check for registration with## @return [Bool]## @api publicdefkey?(key)config.resolver.key?(_container,key)end# An array of registered names for the container## @return [Array<String>]## @api publicdefkeysconfig.resolver.keys(_container)end# Calls block once for each key in container, passing the key as a parameter.## If no block is given, an enumerator is returned instead.## @return [Dry::Core::Container::Mixin] self## @api publicdefeach_key(&)config.resolver.each_key(_container,&)selfend# 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.## @return [Enumerator]## @api public## @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.defeach(&)config.resolver.each(_container,&)end# Decorates an item from the container with specified decorator## @return [Dry::Core::Container::Mixin] self## @api publicdefdecorate(key,with: nil,&block)key=key.to_soriginal=_container.delete(key)doraiseKeyError,"Nothing registered with the key #{key.inspect}"endifwith.is_a?(Class)decorator=with.method(:new)elsifblock.nil?&&!with.respond_to?(:call)raiseError,"Decorator needs to be a Class, block, or respond to the `call` method"elsedecorator=with||blockend_container[key]=original.map(decorator)selfend# Evaluate block and register items in namespace## @param [Mixed] namespace# The namespace to register items in## @return [Dry::Core::Container::Mixin] self## @api publicdefnamespace(namespace,&)::Dry::Core::Container::NamespaceDSL.new(self,namespace,config.namespace_separator,&)selfend# Import a namespace## @param [Dry::Core::Container::Namespace] namespace# The namespace to import## @return [Dry::Core::Container::Mixin] self## @api publicdefimport(namespace)namespace(namespace.name,&namespace.block)selfend# Freeze the container. Nothing can be registered after freezing## @api publicdeffreezesuper_container.freezeselfend# @private no, reallydef_container@_containerend# @api publicdefdupcopy=supercopy.instance_variable_set(:@_container,_container.dup)copyend# @api publicdefclonecopy=superunlesscopy.frozen?copy.instance_variable_set(:@_container,_container.dup)endcopyendend# rubocop:enable Metrics/ModuleLengthendendend