File: dim.rb

Project: DIM: Dependency Injection / Minimal

#!/usr/bin/env ruby
#--
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
#
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
#
# = Dependency Injection - Minimal (DIM)
#
# The DIM module provides a minimal dependency injection framework for
# Ruby programs.
#
# Example:
#
#   require 'dim'
#
#   container = DIM::Container.new
#   container.register(:log_file) { "logfile.log" }
#   container.register(:logger) { |c| FileLogger.new(c.log_file) }
#   container.register(:application) { |c|
#     app = Application.new
#     app.logger = c.logger
#     app
#   }
#
#   c.application.run
#
module DIM
  # Thrown when a service cannot be located by name.
  class MissingServiceError < StandardError; end

  # Thrown when a duplicate service is registered.
  class DuplicateServiceError < StandardError; end

  # DIM::Container is the central data store for registering services
  # used for dependency injuction.  Users register services by
  # providing a name and a block used to create the service.  Services
  # may be retrieved by asking for them by name (via the [] operator)
  # or by selector (via the method_missing technique).
  #
  class Container
    # Create a dependency injection container.  Specify a parent
    # container to use as a fallback for service lookup.
    def initialize(parent=nil)
      @services = {}
      @cache = {}
      @parent = parent || Container
    end

    # Register a service named +name+.  The +block+ will be used to
    # create the service on demand.  It is recommended that symbols be
    # used as the name of a service.
    def register(name, &block)
      if @services[name]
        fail DuplicateServiceError, "Duplicate Service Name '#{name}'"
      end
      @services[name] = block
    end

    # Lookup a service by name.  Throw an exception if no service is
    # found.
    def [](name)
      @cache[name] ||= service_block(name).call(self)
    end

    # Lookup a service by message selector.  A service with the same
    # name as +sym+ will be returned, or an exception is thrown if no
    # matching service is found.
    def method_missing(sym, *args, &block)
      self[sym]
    end

    # Return the block that creates the named service.  Throw an
    # exception if no service creation block of the given name can be
    # found in the container or its parents.
    def service_block(name)
      @services[name] || @parent.service_block(name)
    end

    # Searching for a service block only reaches the Container class
    # when all the containers in the hierarchy search chain have no
    # entry for the service.  In this case, the only thing to do is
    # signal a failure.
    def self.service_block(name)
      fail(MissingServiceError, "Unknown Service '#{name}'")
    end
  end
end

Used by: test_dim


[ Index ][ Presentation ]
Generated by [ source2html ]