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 51
51:     def get(name, optional_properties = {})
52:       raise ArgumentError.new("#{name} is not a registered service name") unless registered?(name)
53:       service_registration = @services[name]
54:       service = service_registration.service.is_a?(Class) ? service_registration.service.new : service_registration.service
55: 
56:       dependencies(name).each do |dependency|
57:         service.send("#{dependency}=", get(dependency, optional_properties))
58:       end
59: 
60:       optional_instances = {}
61:       optional_properties.each_pair do |k,v|
62:         instance = v.is_a?(Class) ? v.new : v
63:         optional_instances[k] = instance
64:       end
65: 
66:       optional_instances.each_pair do |k, v|
67:         writer = "#{k}="
68:         service.send(writer, v) if service.respond_to?(writer)
69:         optional_instances.each_pair do |k2,v2|
70:           next if k2 == k || !v2.respond_to?(writer)
71:           v2.send(writer, v)
72:         end
73:       end
74: 
75:       service_registration.initializers.each do |initializer|
76:         initializer.call(service)
77:       end
78: 
79:       service
80:     end

method_missing(method, *args, &block)

      # File lib/harbor/container.rb, line 82
82:     def method_missing(method, *args, &block)
83:       if method.to_s =~ /^(.*)\=$/
84:         register($1, *args, &block)
85:       else
86:         if registered?(method.to_s)
87:           get(method.to_s, args[0] || {})
88:         else
89:           raise NoMethodError.new("undefined method '#{method}' for #{self}", method)
90:         end
91:       end
92:     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 104
104:     def register(name, service, &setup)
105: 
106:       type_dependencies = dependencies(name)
107:       type_methods = service.is_a?(Class) ? service.instance_methods.grep(/\=$/) : []
108: 
109:       @services.values.each do |service_registration|
110:         if service_registration.service.is_a?(Class) && service_registration.service.instance_methods.include?("#{name}=")
111:           dependencies(service_registration.name) << name
112:         end
113: 
114:         if type_methods.include?("#{service_registration.name}=")
115:           type_dependencies << service_registration.name
116:         end
117:       end
118: 
119:       @services[name] = ServiceRegistration.new(name, service)
120:       @services[name].initializers << setup if setup
121: 
122:       self
123:     end

registered?(name)

       # File lib/harbor/container.rb, line 125
125:     def registered?(name)
126:       @services.key?(name)
127:     end

Private Instance Methods

dependencies(service)

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