Class: Harbor::Container

Inherits:
Object
  • Object
show all
Defined in:
lib/harbor/container.rb

Overview

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>>

Defined Under Namespace

Classes: RegistrationTypeMismatchError, ServiceRegistration

Instance Method Summary collapse

Constructor Details

#initializeContainer

:nodoc:



44
45
46
47
# File 'lib/harbor/container.rb', line 44

def initialize #:nodoc:
  @services = {}
  @dependencies = {}
end

Instance Method Details

#get(name, optional_properties = {}) ⇒ Object

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))

Raises:

  • (ArgumentError)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/harbor/container.rb', line 60

def get(name, optional_properties = {})
  raise ArgumentError.new("#{name} is not a registered service name") unless registered?(name)
  service_registration = @services[name]
  service = service_registration.service.is_a?(Class) ? service_registration.service.new : service_registration.service

  dependencies(name).each do |dependency|
    service.send("#{dependency}=", get(dependency, optional_properties))
  end

  optional_instances = {}
  optional_properties.each_pair do |k,v|
    instance = v.is_a?(Class) ? v.new : v
    optional_instances[k] = instance
  end

  optional_instances.each_pair do |k, v|
    writer = "#{k}="
    service.send(writer, v) if service.respond_to?(writer)
    optional_instances.each_pair do |k2,v2|
      next if k2 == k || !v2.respond_to?(writer)
      v2.send(writer, v)
    end
  end

  service_registration.initializers.each do |initializer|
    initializer.call(service)
  end

  service
end

#register(name, service, &setup) ⇒ Object

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 = "[email protected]" }
services.get("mailer") # => #<Harbor::Mailer @from="[email protected]" @mail_server=#<SendmailServer...>>


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/harbor/container.rb', line 101

def register(name, service, &setup)
  if (existing_registration = @services[name]) && existing_registration.service != service
    raise RegistrationTypeMismatchError.new(name, existing_registration.service, service)
  end

  type_dependencies = dependencies(name)
  type_methods = service.is_a?(Class) ? service.instance_methods.grep(/\=$/) : []

  @services.values.each do |service_registration|
    if service_registration.service.is_a?(Class) && service_registration.service.instance_methods.include?("#{name}=")
      dependencies(service_registration.name) << name
    end

    if type_methods.include?("#{service_registration.name}=")
      type_dependencies << service_registration.name
    end
  end

  @services[name] ||= ServiceRegistration.new(name, service)
  @services[name].initializers << setup if setup

  self
end

#registered?(name) ⇒ Boolean

Returns:

  • (Boolean)


125
126
127
# File 'lib/harbor/container.rb', line 125

def registered?(name)
  @services.key?(name)
end