Harbor Documentation

Harbor::Container

Harbor::Container is an inversion of control container for simple dependency injection. For more information on dependency injection, see martinfowler.com/articles/injection.html.

Simple Example:

  services = Harbor::Container.new
  services.register("mailer", Harbor::Mailer)

  class Controller
    attr_accessor :mailer
  end

  services.register("Controller", Controller)

  services.get("Controller") # => #<Controller: @mailer=#<Mailer>>

Public Instance Methods

get(name, optional_properties = {})

Retrieve a service by name from the set of registered services, initializing any dependencies from the container, and optionally setting any additional properties on the service.

  class Controller
    attr_accessor :request, :response, :mailer
  end

  services.get("Controller", :request => Request.new(env), :response => Response.new(request))
      # File lib/harbor/container.rb, line 61
61:     def get(name, optional_properties = {})
62:       raise ArgumentError.new("#{name} is not a registered service name") unless registered?(name)
63:       service_registration = @services[name]
64:       service = service_registration.service.is_a?(Class) ? service_registration.service.new : service_registration.service
65: 
66:       dependencies(name).each do |dependency|
67:         service.send("#{dependency}=", get(dependency, optional_properties))
68:       end
69: 
70:       optional_instances = {}
71:       optional_properties.each_pair do |k,v|
72:         instance = v.is_a?(Class) ? v.new : v
73:         optional_instances[k] = instance
74:       end
75: 
76:       optional_instances.each_pair do |k, v|
77:         writer = "#{k}="
78:         service.send(writer, v) if service.respond_to?(writer)
79:         optional_instances.each_pair do |k2,v2|
80:           next if k2 == k || !v2.respond_to?(writer)
81:           v2.send(writer, v)
82:         end
83:       end
84: 
85:       service_registration.initializers.each do |initializer|
86:         initializer.call(service)
87:       end
88: 
89:       service
90:     end

method_missing(method, *args, &block)

      # File lib/harbor/container.rb, line 92
92:     def method_missing(method, *args, &block)
93:       if registered?(method.to_s)
94:         get(method.to_s, args[0] || {})
95:       else
96:         raise NoMethodError.new("undefined method '#{method}' for #{self}", method)
97:       end
98:     end

register(name, service, &setup)

Register a service by name, with an optional initializer block.

  services.register("mail_server", Harbor::SendmailServer.new(:sendmail => "/sbin/sendmail"))
  services.register("mailer", Harbor::Mailer)
  services.get("mailer") # => #<Harbor::Mailer @from=nil @mail_server=#<SendmailServer...>>

  services.register("mailer", Harbor::Mailer) { |mailer| mailer.from = "admin@example.com" }
  services.get("mailer") # => #<Harbor::Mailer @from="admin@example.com" @mail_server=#<SendmailServer...>>
       # File lib/harbor/container.rb, line 110
110:     def register(name, service, &setup)
111:       if (existing_registration = @services[name]) && existing_registration.service != service
112:         raise RegistrationTypeMismatchError.new(name, existing_registration.service, service)
113:       end
114: 
115:       type_dependencies = dependencies(name)
116:       type_methods = service.is_a?(Class) ? service.instance_methods.grep(/\=$/) : []
117: 
118:       @services.values.each do |service_registration|
119:         if service_registration.service.is_a?(Class) && service_registration.service.instance_methods.include?("#{name}=")
120:           dependencies(service_registration.name) << name
121:         end
122: 
123:         if type_methods.include?("#{service_registration.name}=")
124:           type_dependencies << service_registration.name
125:         end
126:       end
127: 
128:       @services[name] ||= ServiceRegistration.new(name, service)
129:       @services[name].initializers << setup if setup
130: 
131:       self
132:     end

registered?(name)

       # File lib/harbor/container.rb, line 134
134:     def registered?(name)
135:       @services.key?(name)
136:     end

Private Instance Methods

dependencies(service)

       # File lib/harbor/container.rb, line 139
139:     def dependencies(service)
140:       @dependencies[service] ||= Set.new
141:     end