lib/dependency_injection/definition.rb
require 'active_support/core_ext/string/inflections'
require 'dependency_injection/scope_widening_injection_error'
module DependencyInjection
class Definition
attr_accessor :arguments, :configurator, :file_path, :klass_name, :method_calls, :scope
def initialize(klass_name, container)
@container = container
self.arguments = []
self.file_path = nil
self.klass_name = klass_name
self.method_calls = {}
self.scope = :container
end
def add_argument(argument)
self.add_arguments(argument)
end
def add_arguments(*arguments)
self.arguments += arguments
self
end
def add_configurator(name, method_name)
self.configurator = [name, method_name]
self
end
def add_method_call(method_name, *arguments)
self.method_calls[method_name] = arguments
self
end
def klass
self.klass_name.constantize
end
def object
self.send("#{self.scope}_scoped_object")
end
private
def container_scoped_object
@object ||= initialize_object
end
def initialize_object
require_object
object = self.klass.new(*resolve(self.arguments))
self.method_calls.each { |method_name, arguments| object.send(method_name, *resolve(arguments)) }
if self.configurator
name, method_name = self.configurator
configurator_object = resolve([name]).first
configurator_object.send(method_name, object)
end
object
end
def object_already_required?
true if Kernel.const_get(self.klass_name)
rescue
false
end
def prototype_scoped_object
initialize_object
end
def require_object
return if object_already_required?
if self.file_path
require self.file_path
else
require self.klass_name.underscore
end
end
def resolve(arguments)
resolve_references(resolve_container_parameters(arguments))
end
def resolve_container_parameters(argument)
if argument.kind_of?(Array)
argument.map { |arg| resolve(arg) }
elsif /^%(?<parameter_name>.*)%$/ =~ argument.to_s
@container.parameters[parameter_name]
else
argument
end
end
def resolve_references(argument)
if argument.kind_of?(Array)
argument.map { |arg| resolve(arg) }
elsif /^@(?<reference_name>.*)/ =~ argument.to_s
reference_definition = @container.find(reference_name)
reference = reference_definition.object
raise ScopeWideningInjectionError if reference_definition.scope == :prototype && scope == :container
reference
else
argument
end
end
end
end