dim 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/MIT-LICENSE +21 -0
- data/README.md +16 -0
- data/Rakefile +10 -0
- data/dim.gemspec +21 -0
- data/lib/dim.rb +92 -0
- data/lib/dim/version.rb +3 -0
- data/spec/dim_spec.rb +165 -0
- metadata +76 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-I. -I/Users/jim/working/git/rspec-given/lib --color
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@dim --create
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2004, 2010 Jim Weirich
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# DIM: Dependency Injection - Minimal
|
2
|
+
|
3
|
+
DIM is Jim Weirich's minimalistic dependency injection framework,
|
4
|
+
maintained in Gem form by [Mike Subelsky](http://subelsky.com).
|
5
|
+
For more background information, read Jim's [Dependency Injection](http://onestepback.org/articles/depinj/dim/dim_rb.html))
|
6
|
+
slides.
|
7
|
+
|
8
|
+
# License
|
9
|
+
|
10
|
+
DIM is available under the MIT license (see the file MIT-LICENSE for
|
11
|
+
details).
|
12
|
+
|
13
|
+
# More Information:
|
14
|
+
|
15
|
+
Read [this](http://onestepback.org/articles/depinj/appendixa.html) for
|
16
|
+
more information about using DIM.
|
data/Rakefile
ADDED
data/dim.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require './lib/dim/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{dim}
|
5
|
+
s.version = Dim::VERSION
|
6
|
+
s.authors = ["Jim Weirich", "Mike Subelsky"]
|
7
|
+
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
8
|
+
s.email = %q{mike@subelsky.com}
|
9
|
+
s.extra_rdoc_files = [
|
10
|
+
"README.md"
|
11
|
+
]
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.homepage = %q{http://github.com/subelsky/dim}
|
14
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubygems_version = %q{1.5.0}
|
17
|
+
s.summary = %q{Minimalistic dependency injection framework}
|
18
|
+
s.description = %q{Minimalistic dependency injection framework}
|
19
|
+
s.test_files = `git ls-files spec`.split("\n")
|
20
|
+
s.add_development_dependency 'rspec'
|
21
|
+
end
|
data/lib/dim.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright 2004, 2005, 2010 by Jim Weirich (jim.weirich@gmail.com).
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This software is available under the MIT license. See the
|
7
|
+
# MIT-LICENSE file for details.
|
8
|
+
#++
|
9
|
+
#
|
10
|
+
# = Dependency Injection - Minimal (DIM)
|
11
|
+
#
|
12
|
+
# The DIM module provides a minimal dependency injection framework for
|
13
|
+
# Ruby programs.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
#
|
17
|
+
# require 'dim'
|
18
|
+
#
|
19
|
+
# container = Dim::Container.new
|
20
|
+
# container.register(:log_file) { "logfile.log" }
|
21
|
+
# container.register(:logger) { |c| FileLogger.new(c.log_file) }
|
22
|
+
# container.register(:application) { |c|
|
23
|
+
# app = Application.new
|
24
|
+
# app.logger = c.logger
|
25
|
+
# app
|
26
|
+
# }
|
27
|
+
#
|
28
|
+
# c.application.run
|
29
|
+
#
|
30
|
+
module Dim
|
31
|
+
# Thrown when a service cannot be located by name.
|
32
|
+
class MissingServiceError < StandardError; end
|
33
|
+
|
34
|
+
# Thrown when a duplicate service is registered.
|
35
|
+
class DuplicateServiceError < StandardError; end
|
36
|
+
|
37
|
+
# Dim::Container is the central data store for registering services
|
38
|
+
# used for dependency injuction. Users register services by
|
39
|
+
# providing a name and a block used to create the service. Services
|
40
|
+
# may be retrieved by asking for them by name (via the [] operator)
|
41
|
+
# or by selector (via the method_missing technique).
|
42
|
+
#
|
43
|
+
class Container
|
44
|
+
attr_reader :parent
|
45
|
+
|
46
|
+
# Create a dependency injection container. Specify a parent
|
47
|
+
# container to use as a fallback for service lookup.
|
48
|
+
def initialize(parent=nil)
|
49
|
+
@services = {}
|
50
|
+
@cache = {}
|
51
|
+
@parent = parent || Container
|
52
|
+
end
|
53
|
+
|
54
|
+
# Register a service named +name+. The +block+ will be used to
|
55
|
+
# create the service on demand. It is recommended that symbols be
|
56
|
+
# used as the name of a service.
|
57
|
+
def register(name, &block)
|
58
|
+
if @services[name]
|
59
|
+
fail DuplicateServiceError, "Duplicate Service Name '#{name}'"
|
60
|
+
end
|
61
|
+
@services[name] = block
|
62
|
+
end
|
63
|
+
|
64
|
+
# Lookup a service by name. Throw an exception if no service is
|
65
|
+
# found.
|
66
|
+
def [](name)
|
67
|
+
@cache[name] ||= service_block(name).call(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Lookup a service by message selector. A service with the same
|
71
|
+
# name as +sym+ will be returned, or an exception is thrown if no
|
72
|
+
# matching service is found.
|
73
|
+
def method_missing(sym, *args, &block)
|
74
|
+
self[sym]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the block that creates the named service. Throw an
|
78
|
+
# exception if no service creation block of the given name can be
|
79
|
+
# found in the container or its parents.
|
80
|
+
def service_block(name)
|
81
|
+
@services[name] || @parent.service_block(name)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Searching for a service block only reaches the Container class
|
85
|
+
# when all the containers in the hierarchy search chain have no
|
86
|
+
# entry for the service. In this case, the only thing to do is
|
87
|
+
# signal a failure.
|
88
|
+
def self.service_block(name)
|
89
|
+
fail(MissingServiceError, "Unknown Service '#{name}'")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/dim/version.rb
ADDED
data/spec/dim_spec.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'dim'
|
2
|
+
require 'rspec/given'
|
3
|
+
|
4
|
+
class ConsoleAppender
|
5
|
+
end
|
6
|
+
|
7
|
+
class Logger
|
8
|
+
attr_accessor :appender
|
9
|
+
end
|
10
|
+
|
11
|
+
class MockDB
|
12
|
+
end
|
13
|
+
|
14
|
+
class RealDB
|
15
|
+
attr_accessor :username, :password
|
16
|
+
def initialize(username, password)
|
17
|
+
@username, @password = username, password
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class App
|
22
|
+
attr_accessor :logger, :db
|
23
|
+
def initialize(logger=nil)
|
24
|
+
@logger = logger
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe Dim::Container do
|
29
|
+
let(:container) { Dim::Container.new }
|
30
|
+
|
31
|
+
Scenario "creating objects" do
|
32
|
+
Given { container.register(:app) { App.new } }
|
33
|
+
Then { container.app.should be_a(App) }
|
34
|
+
end
|
35
|
+
|
36
|
+
Scenario "returning the same object every time" do
|
37
|
+
Given { container.register(:app) { App.new } }
|
38
|
+
Given(:app) { container.app }
|
39
|
+
Then { container.app.should be(app) }
|
40
|
+
end
|
41
|
+
|
42
|
+
Scenario "contructing dependent objects" do
|
43
|
+
Given { container.register(:app) { |c| App.new(c.logger) } }
|
44
|
+
Given { container.register(:logger) { Logger.new } }
|
45
|
+
Given(:app) { container.app }
|
46
|
+
Then { app.logger.should be(container.logger) }
|
47
|
+
end
|
48
|
+
|
49
|
+
Scenario "constructing dependent objects with setters" do
|
50
|
+
Given {
|
51
|
+
container.register(:app) { |c|
|
52
|
+
App.new.tap { |obj|
|
53
|
+
obj.db = c.database
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
Given { container.register(:database) { MockDB.new } }
|
58
|
+
Given(:app) { container.app }
|
59
|
+
|
60
|
+
Then { app.db.should be(container.database) }
|
61
|
+
end
|
62
|
+
|
63
|
+
Scenario "constructing multiple dependent objects" do
|
64
|
+
Given {
|
65
|
+
container.register(:app) { |c|
|
66
|
+
App.new(c.logger).tap { |obj|
|
67
|
+
obj.db = c.database
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
Given { container.register(:logger) { Logger.new } }
|
72
|
+
Given { container.register(:database) { MockDB.new } }
|
73
|
+
Given(:app) { container.app }
|
74
|
+
Then { app.logger.should be(container.logger) }
|
75
|
+
Then { app.db.should be(container.database) }
|
76
|
+
end
|
77
|
+
|
78
|
+
Scenario "constructing chains of dependencies" do
|
79
|
+
Given { container.register(:app) { |c| App.new(c.logger) } }
|
80
|
+
Given {
|
81
|
+
container.register(:logger) { |c|
|
82
|
+
Logger.new.tap { |obj|
|
83
|
+
obj.appender = c.logger_appender
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
Given { container.register(:logger_appender) { ConsoleAppender.new } }
|
88
|
+
Given { container.register(:database) { MockDB.new } }
|
89
|
+
Given(:logger) { container.app.logger }
|
90
|
+
|
91
|
+
Then { logger.appender.should be(container.logger_appender) }
|
92
|
+
end
|
93
|
+
|
94
|
+
Scenario "constructing literals" do
|
95
|
+
Given { container.register(:database) { |c| RealDB.new(c.username, c.userpassword) } }
|
96
|
+
Given { container.register(:username) { "user_name_value" } }
|
97
|
+
Given { container.register(:userpassword) { "password_value" } }
|
98
|
+
Given(:db) { container.database }
|
99
|
+
|
100
|
+
Then { db.username.should == "user_name_value" }
|
101
|
+
Then { db.password.should == "password_value" }
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "Errors" do
|
105
|
+
Scenario "missing services" do
|
106
|
+
Then {
|
107
|
+
lambda {
|
108
|
+
container.undefined_service_name
|
109
|
+
}.should raise_error(Dim::MissingServiceError, /undefined_service_name/)
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
Scenario "duplicate service names" do
|
114
|
+
Given { container.register(:duplicate_name) { 0 } }
|
115
|
+
Then {
|
116
|
+
lambda {
|
117
|
+
container.register(:duplicate_name) { 0 }
|
118
|
+
}.should raise_error(Dim::DuplicateServiceError, /duplicate_name/)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "Parent/Child Container Interaction" do
|
124
|
+
Given(:parent) { container }
|
125
|
+
Given(:child) { Dim::Container.new(parent) }
|
126
|
+
|
127
|
+
Given { parent.register(:cell) { :parent_cell } }
|
128
|
+
Given { parent.register(:gene) { :parent_gene } }
|
129
|
+
Given { child.register(:gene) { :child_gene } }
|
130
|
+
|
131
|
+
Scenario "reusing a service from the parent" do
|
132
|
+
Then { child.cell.should == :parent_cell }
|
133
|
+
end
|
134
|
+
|
135
|
+
Scenario "overiding a service from the parent" do
|
136
|
+
Then "the child service overrides the parent" do
|
137
|
+
child.gene.should == :child_gene
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
Scenario "wrapping a service from a parent" do
|
142
|
+
Given { child.register(:cell) { |c| [c.parent.cell] } }
|
143
|
+
Then { child.cell.should == [:parent_cell] }
|
144
|
+
end
|
145
|
+
|
146
|
+
Scenario "overriding an indirect dependency" do
|
147
|
+
Given { parent.register(:wrapped_cell) { |c| [c.cell] } }
|
148
|
+
Given { child.register(:cell) { :child_cell } }
|
149
|
+
Then { child.wrapped_cell.should == [:child_cell] }
|
150
|
+
end
|
151
|
+
|
152
|
+
Scenario "parent / child service conflicts from parents view" do
|
153
|
+
Then { parent.gene.should == :parent_gene }
|
154
|
+
end
|
155
|
+
|
156
|
+
Scenario "child / child service name conflicts" do
|
157
|
+
Given(:other_child) { Dim::Container.new(parent) }
|
158
|
+
Given { other_child.register(:gene) { :other_child_gene } }
|
159
|
+
|
160
|
+
Then { child.gene.should == :child_gene }
|
161
|
+
Then { other_child.gene.should == :other_child_gene }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dim
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: "0.9"
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jim Weirich
|
9
|
+
- Mike Subelsky
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2011-02-08 00:00:00 -05:00
|
15
|
+
default_executable:
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: rspec
|
19
|
+
prerelease: false
|
20
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
21
|
+
none: false
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: "0"
|
26
|
+
type: :development
|
27
|
+
version_requirements: *id001
|
28
|
+
description: Minimalistic dependency injection framework
|
29
|
+
email: mike@subelsky.com
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files:
|
35
|
+
- README.md
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- .rspec
|
39
|
+
- .rvmrc
|
40
|
+
- MIT-LICENSE
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- dim.gemspec
|
44
|
+
- lib/dim.rb
|
45
|
+
- lib/dim/version.rb
|
46
|
+
- spec/dim_spec.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/subelsky/dim
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- --charset=UTF-8
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.5.0
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Minimalistic dependency injection framework
|
75
|
+
test_files:
|
76
|
+
- spec/dim_spec.rb
|