Skip to content

Commit

Permalink
Implemented simple dependency injection based only on dependency name.
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Pohorecki committed Apr 14, 2011
1 parent 66e4db3 commit 0ffff63
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 38 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,3 +2,4 @@ pkg/*
*.gem
.bundle
.rvmrc
tags
2 changes: 2 additions & 0 deletions Rakefile
Expand Up @@ -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
13 changes: 11 additions & 2 deletions lib/dependor.rb
@@ -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
20 changes: 20 additions & 0 deletions lib/dependor/dependency_to_class_name_converter.rb
@@ -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
2 changes: 1 addition & 1 deletion lib/dependor/injectable.rb
Expand Up @@ -8,7 +8,7 @@ def self.included(klass)

module InstanceMethods
def inject!
self
Dependor.injector.inject(self)
end
end

Expand Down
35 changes: 35 additions & 0 deletions lib/dependor/injector.rb
@@ -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
2 changes: 2 additions & 0 deletions lib/dependor/meta_data.rb
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions spec/dependor/dependency_to_class_name_converter_spec.rb
@@ -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
36 changes: 1 addition & 35 deletions spec/dependor/injectable_spec.rb
Expand Up @@ -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 }

Expand Down Expand Up @@ -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)
Expand Down
43 changes: 43 additions & 0 deletions spec/dependor/injector_spec.rb
@@ -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
16 changes: 16 additions & 0 deletions spec/dependor/meta_data_spec.rb
Expand Up @@ -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
33 changes: 33 additions & 0 deletions spec/sample_classes.rb
@@ -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
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -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.