Extend a class (or module) with Container to make the class behave as a class of service containers. Containers encapsulate the way in which related services are instantiated and connected together. Services themselves are insulated from this process [except when they are injected with the container].

Use MinDI by includeing the MinDI::InjectableContainer module in your container class. As of version 0.2, this does two things: it extends your container class with the Container module, which provides class methods for expressing service definitions in a natural, rubylike way. It also injects the container into all services which gives them direct access to the other services in the container. If you prefer to have the container funtionality without injecting services (i.e., just "contructor injection") You can just include MinDI::BasicContainer.

The Container module also defines shortcuts for defining some service types using method_missing. These methods and shortcuts are described below.

A service definition involves a service name and a block of code. It defines an instance method whose name is the service name. The block is used to instantiate the service. When and how that service is instantiated depends on the type of service.

Note: MinDI uses some instance variables and auxiliary instance methods in the container. The instance variables have the same name as the corresponding services, possibly with added suffixes, as in the case of deferred. The auxiliary methods are named so as to make conflicts unlikely. See the implemetations of iv and impl_method_name for details.

Note that services can be defined dynamically by reopening the class scope or simply by calling the service with a block. See examples/dynamic.rb.

Contents
Methods

Methods which define a service in terms of simple rules involving existence, uniqeness, and parameters.

Public Instance methods
generic(name) {|| ...}

Define a generic service, which has no built-in rules for existence or uniqueness. There is no shortcut for generic service definition. Calling the service simply calls the associated block. This is also known as a prototype service.

# File lib/mindi.rb, line 48
    def generic(name, &impl)  # :yields:
      define_implementation(name, impl)
    end
multikey_multiton(name) {|arg0, arg1, ...| ...}

Define a multiton service with multiple keys:

  multiton(:service_name) { |arg0, arg1, ...| ... }

The block will be called once per distinct (in the sense of hash keys) argument list to instantiate a unique value corresponding to the argument list. The shortcut for defining a multikey_multiton with multiple keys is:

  service_name { |arg0, arg1, ...| ... }

The service is invoked as service_name(arg0, arg1, …). Variable length argument lists, using the splat notation, are permitted.

# File lib/mindi.rb, line 113
    def multikey_multiton(name, &impl) # :yields: arg0, arg1, ...
      impl_name = Container.impl_method_name(name)
      define_implementation(impl_name, impl)

      ivname = Container.iv(name)
      define_method(name) do |*key|
        map = instance_variable_get(ivname)
        map ||= instance_variable_set(ivname, {})
        map.key?(key) ?  map[key] : map[key] = send(impl_name, *key)
      end
    end
multiton(name) {|arg| ...}

Define a multiton service:

  multiton(:service_name) { |arg| ... }

The block will be called once per distinct (in the sense of hash keys) argument to instantiate a unique value corresponding to the argument. The shortcut for defining a multiton is:

  service_name { |arg| ... }

The service is invoked as service_name(arg).

# File lib/mindi.rb, line 88
    def multiton(name, &impl) # :yields: arg
      impl_name = Container.impl_method_name(name)
      define_implementation(impl_name, impl)
      
      ivname = Container.iv(name)
      define_method(name) do |key|
        map = instance_variable_get(ivname)
        map ||= instance_variable_set(ivname, {})
        map.key?(key) ?  map[key] : map[key] = send(impl_name, key)
      end
    end
singleton(name) {|| ...}

Define a singleton service:

  singleton(:service_name) { ... }

The block will be called at most once to instantiate a unique value for the service. The shortcut for defining a singleton is:

  service_name { ... }

The service is invoked as service_name.

# File lib/mindi.rb, line 63
    def singleton(name, &impl)  # :yields:
      impl_name = Container.impl_method_name(name)
      define_implementation(impl_name, impl)

      ivname = Container.iv(name)
      define_method(name) do
        box = instance_variable_get(ivname)
        box ||= instance_variable_set(ivname, [])
        box << send(impl_name) if box.empty?
        box.first
      end
    end

Methods which define a service in terms of more complex modalities, such as per-thread uniqueness, and deferred, on-demand existence.

Public Instance methods
deferred(name) {|| ...}

Define a singleton service with deferred instantiation. Syntax and semantics are the same as singleton, except that the block is not called when the service is requested, but only when a method is called on the service.

# File lib/mindi.rb, line 160
    def deferred(name, &impl)  # :yields:
      impl_name = Container.impl_method_name(name)
      define_implementation(impl_name, impl)
      
      proxy_name = Container.impl_method_name("#{name}_proxy")

      ivname = Container.iv(name)
      proxy_ivname = Container.iv("#{name}_proxy")
      
      define_method(name) do
        instance_variable_get(ivname) || send(proxy_name)
      end
      
      define_method(proxy_name) do
        proxy = instance_variable_get(proxy_ivname)
        
        unless proxy
          proxy = proc {instance_variable_set(ivname, send(impl_name))}
          def proxy.method_missing(*args, &block)
            call.__send__(*args, &block)
          end
          instance_variable_set(proxy_ivname, proxy)
          class << proxy; self; end.class_eval do
            (proxy.methods - PROXY_METHODS).each do |m|
              undef_method m
            end
          end
        end

        proxy
      end
    end
threaded(name) {|thr| ...}

Define a service with per-thread instantiation. For each thread, the service appears to be a singleton service. The block will be called at most once per thread. There is no shortcut. The block may take a single argument, in which case it will be passed the current thread.

  threaded(:service_name) { |thr| ... }
# File lib/mindi.rb, line 139
    def threaded(name, &impl)  # :yields: thr
      impl_name = Container.impl_method_name(name)
      define_implementation(impl_name, impl)
      arity = impl.arity

      ivname = Container.iv(name)
      define_method(name) do
        key = Thread.current
        map = instance_variable_get(ivname)
        map ||= instance_variable_set(ivname, {})
        map[key] ||= (arity == 1 ? send(impl_name, key) : send(impl_name))
      end
    end
Protected Class methods
impl_method_name(name)

The name of a method used internally to implement the service named name.

# File lib/mindi.rb, line 232
    def self.impl_method_name(name) # :doc:
      "___#{name}___implementation"
    end
iv(name)

The name of an instance variable that stores the state of the service named name.

# File lib/mindi.rb, line 226
    def self.iv(name) # :doc:
      "@#{name}"
    end
Protected Instance methods
define_implementation(impl_name, impl)
# File lib/mindi.rb, line 212
    def define_implementation impl_name, impl
      if @__services_are_injected__
        preinject_method = impl_name + "__preinject"
        define_method(preinject_method, &impl)
        define_method(impl_name) do |*args|
          inject_into send(preinject_method, *args)
        end
      else
        define_method(impl_name, &impl)
      end
    end