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