Permalink
Browse files

Implemented simple dependency injection based only on dependency name.

  • Loading branch information...
1 parent 66e4db3 commit 0ffff634e69e3022f6d4824cc70f81f347ebd76c @psyho committed Apr 14, 2011
View
@@ -2,3 +2,4 @@ pkg/*
*.gem
.bundle
.rvmrc
+tags
View
@@ -9,3 +9,5 @@ RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = "./spec/**/*_spec.rb"
t.rspec_opts = ["--profile --color --format=documentation"]
end
+
+task :default => :spec
View
@@ -1,8 +1,17 @@
require 'dependor/injectable.rb'
require 'dependor/meta_data.rb'
+require 'dependor/injector.rb'
+require 'dependor/dependency_to_class_name_converter.rb'
-module Fake
+module Dependor
+ def self.dependency_to_class_name_converter
+ @dependency_to_class_name_converter ||= DependencyToClassNameConverter.new
+ end
+
+ def self.injector
+ @injector ||= Injector.new(dependency_to_class_name_converter)
+ end
end
-module Dependor
+module Fake
end
@@ -0,0 +1,20 @@
+module Dependor
+ class DependencyToClassNameConverter
+
+ def convert(dependency_name)
+ class_name = dashes_to_colons(dependency_name.to_s)
+ return camelize(class_name)
+ end
+
+ private
+
+ def camelize(string)
+ string.gsub(/^[a-z]/){|s| s.upcase}.gsub(/_+/, '_').gsub(/_[a-z]/){|s| s[1].upcase}
+ end
+
+ def dashes_to_colons(string)
+ string.gsub('-', '::_')
+ end
+
+ end
+end
@@ -8,7 +8,7 @@ def self.included(klass)
module InstanceMethods
def inject!
- self
+ Dependor.injector.inject(self)
end
end
View
@@ -0,0 +1,35 @@
+module Dependor
+ class Injector
+
+ attr_reader :dependency_to_class_name_converter
+
+ def initialize(dependency_to_class_name_converter)
+ @dependency_to_class_name_converter = dependency_to_class_name_converter
+ end
+
+ def inject(instance)
+ meta_data = Dependor::MetaData.for(instance)
+
+ meta_data.dependencies.each do |dependency_name|
+ klass = class_for_name(dependency_name)
+ dependency = inject(klass.new)
+ instance.send("#{dependency_name}=", dependency)
+ end
+
+ return instance
+ end
+
+ private
+
+ def class_for_name(dependency_name)
+ class_name = dependency_to_class_name_converter.convert(dependency_name)
+ parts = class_name.split('::')
+ context = Object
+ parts.each do |part|
+ context = context.const_get(part)
+ end
+ return context
+ end
+
+ end
+end
@@ -17,6 +17,8 @@ def dependencies
def self.for(klass)
if klass.respond_to?(:dependor_meta_data)
return klass.dependor_meta_data
+ elsif !klass.is_a?(Class)
+ return self.for(klass.class)
else
return EmptyMetaData.new
end
@@ -0,0 +1,29 @@
+require File.expand_path('../../spec_helper.rb', __FILE__)
+
+describe Dependor::DependencyToClassNameConverter do
+
+ before(:each) do
+ @converter = Dependor::DependencyToClassNameConverter.new
+ end
+
+ it "should convert simple names to camel case" do
+ @converter.convert(:foo).should == "Foo"
+ end
+
+ it "should convert multi part underscore_names to CamelCase" do
+ @converter.convert(:some_component).should == "SomeComponent"
+ end
+
+ it "should convert - to :: in names" do
+ @converter.convert("some_module-some_class").should == "SomeModule::SomeClass"
+ end
+
+ it "should leave camelcase names unchanged" do
+ @converter.convert("SomeClass").should == "SomeClass"
+ end
+
+ it "should leave camelcase names with :: in them unchanged" do
+ @converter.convert("SomeModule::SomeClass").should == "SomeModule::SomeClass"
+ end
+
+end
@@ -2,40 +2,6 @@
describe Dependor::Injectable do
- class Foo
- def name
- "world"
- end
- end
-
- class Fake::Foo
- def name
- "fake"
- end
- end
-
- class Bar
- include Dependor::Injectable
-
- depends_on :foo
-
- def hello
- return "Hello #{foo.name}!"
- end
- end
-
- class Fake::Bar
- def hello
- "Hello?"
- end
- end
-
- class Baz
- include Dependor::Injectable
-
- depends_on :bar
- end
-
describe ".make" do
let(:baz) { Baz.make }
@@ -77,7 +43,7 @@ class DependencyInheritanceChild < DependencyInheritanceParent
end
it "should inject both parent and child dependencies" do
- sample = DependencyInheritanceChild.new
+ sample = DependencyInheritanceChild.make
sample.foo.should be_an_instance_of(Foo)
sample.bar.should be_an_instance_of(Bar)
@@ -0,0 +1,43 @@
+require File.expand_path('../../spec_helper.rb', __FILE__)
+
+describe Dependor::Injector do
+
+ before(:each) do
+ @injector = Dependor::Injector.new(Dependor::DependencyToClassNameConverter.new)
+ end
+
+ describe "for an instance of a class with no dependencies" do
+ class NoDependenciesSample
+ attr_accessor :foo
+ end
+
+ it "should do nothing" do
+ instance = NoDependenciesSample.new
+
+ @injector.inject(instance)
+
+ instance.foo.should be_nil
+ end
+ end
+
+ describe "for an instance of a class with dependencies" do
+ it "should inject those dependencies" do
+ bar = Bar.new
+
+ @injector.inject(bar)
+
+ bar.foo.should be_an_instance_of(Foo)
+ end
+ end
+
+ describe "for a multi-level dependency hierarchy" do
+ it "should inject dependencies of dependencies too" do
+ baz = Baz.new
+
+ @injector.inject(baz)
+
+ baz.bar.foo.should be_an_instance_of(Foo)
+ end
+ end
+
+end
@@ -53,4 +53,20 @@ class InheritingFromClassWithNoDependenciesSample < NoMetaDataSample
end
end
+ describe "for an instance of a class with no dependencies declared" do
+ it "should return empty metadata" do
+ metadata = Dependor::MetaData.for(NoMetaDataSample.new)
+
+ metadata.dependencies.should == Set.new
+ end
+ end
+
+ describe "for an instance of a class with some dependencies declared" do
+ it "should return the same metadata as for the class" do
+ metadata = Dependor::MetaData.for(SomeDependenciesSample.new)
+
+ metadata.dependencies.should == Set.new([:foo, :bar])
+ end
+ end
+
end
View
@@ -0,0 +1,33 @@
+class Foo
+ def name
+ "world"
+ end
+end
+
+class Fake::Foo
+ def name
+ "fake"
+ end
+end
+
+class Bar
+ include Dependor::Injectable
+
+ depends_on :foo
+
+ def hello
+ return "Hello #{foo.name}!"
+ end
+end
+
+class Fake::Bar
+ def hello
+ "Hello?"
+ end
+end
+
+class Baz
+ include Dependor::Injectable
+
+ depends_on :bar
+end
View
@@ -5,6 +5,8 @@
require 'dependor'
+require File.expand_path('../sample_classes.rb', __FILE__)
+
RSpec.configure do |c|
c.color_enabled = true
end

0 comments on commit 0ffff63

Please sign in to comment.