ioc 4.1.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +2 -2
- data/README.md +73 -2
- data/ioc.gemspec +12 -11
- data/lib/ioc.rb +37 -1
- data/spec/ioc_spec.rb +59 -0
- metadata +20 -12
- data/lib/ioc/container.rb +0 -41
- data/lib/ioc/service_block.rb +0 -11
- data/lib/ioc/service_class.rb +0 -16
- data/lib/ioc/unresolvable_container.rb +0 -7
- data/spec/container_spec.rb +0 -130
- data/spec/spec_helper.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45cfdeec3bba0d8515b0cb35b5237b66a028ce85
|
4
|
+
data.tar.gz: 13fcee15a0d44787e8f79017ec0c5cb81c0bf126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b880eeb06d15ddf14edcc384ba270d0ec8d7c23364a043a43af2a1211889909d42531d05bb0cbe7f012489c2407f06bc5af8ce1a831751a3bd708930a051326
|
7
|
+
data.tar.gz: 21b1a31d877de30d7fac725a40208c2e1eeecce806f1e0812eaedd992f95fedad60b68f55bc4c073c3afd5be38334403e88553b836227434cec4d46966823d69
|
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2015 Erik Lott
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# IOC
|
2
2
|
|
3
|
-
|
3
|
+
Minimal dependency injection container for ruby.
|
4
|
+
|
5
|
+
Tested on the following Rubies: MRI 2.2.1, 2.0.0, 1.9.3
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -17,8 +19,77 @@ Or install it yourself as:
|
|
17
19
|
$ gem install ioc
|
18
20
|
|
19
21
|
## Usage
|
22
|
+
### Block Registration
|
23
|
+
This example illustrates how to register a service/component with the container by passing a block to the register method.
|
24
|
+
```ruby
|
25
|
+
require "ioc"
|
26
|
+
|
27
|
+
class AccountRepository
|
28
|
+
def initialize(database, logger)
|
29
|
+
@database = database
|
30
|
+
@logger = logger
|
31
|
+
end
|
32
|
+
# ... some store & finder methods here
|
33
|
+
end
|
34
|
+
|
35
|
+
class UserRepository
|
36
|
+
def initialize(database, logger)
|
37
|
+
@database = database
|
38
|
+
@logger = logger
|
39
|
+
end
|
40
|
+
# ... some store & finder methods here
|
41
|
+
end
|
42
|
+
|
43
|
+
class CreateAccountService
|
44
|
+
def initialize(account_repository, user_repository, logger)
|
45
|
+
@account_repository = account_repository
|
46
|
+
@user_repository = user_repository
|
47
|
+
@logger = logger
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(req, res)
|
51
|
+
# ... create account code
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Register Services
|
56
|
+
container = IOC::Container.new
|
57
|
+
container.register(:create_account_service) do |c|
|
58
|
+
CreateAccountService.new(c.account_repository, c.user_repository, c.logger)
|
59
|
+
end
|
60
|
+
container.register(:user_repository) do |c|
|
61
|
+
UserRepository.new(c.database, c.logger)
|
62
|
+
end
|
63
|
+
container.register(:account_repository) do |c|
|
64
|
+
AccountRepository.new(c.database, c.logger)
|
65
|
+
end
|
66
|
+
container.register(:logger){|c| Logger.new}
|
67
|
+
container.register(:database){|c| DB}
|
68
|
+
|
69
|
+
# Resolve Service
|
70
|
+
container.resolve(:create_account_service).call(req, res)
|
71
|
+
|
72
|
+
# Release the object graph for garbage collection
|
73
|
+
container.release
|
74
|
+
```
|
75
|
+
### Automatic Dependency Resolution
|
76
|
+
Rather than assigning each service's dependencies manually like in the example above, we can allow the container to build and inject each service's dependencies automatically.
|
77
|
+
```ruby
|
78
|
+
# Register Services
|
79
|
+
container = IOC::Container.new
|
80
|
+
container.register(:create_account_service, CreateAccountService)
|
81
|
+
container.register(:user_repository, UserRepository)
|
82
|
+
container.register(:account_repository, AccountRepository)
|
83
|
+
container.register(:logger){|c| Logger.new}
|
84
|
+
container.register(:database){|c| DB}
|
85
|
+
|
86
|
+
# Resolve Service
|
87
|
+
container.resolve(:create_account_service).call(req, res)
|
88
|
+
|
89
|
+
# Release the object graph for garbage collection
|
90
|
+
container.release
|
91
|
+
```
|
20
92
|
|
21
|
-
TODO: Write usage instructions here
|
22
93
|
|
23
94
|
## Contributing
|
24
95
|
|
data/ioc.gemspec
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name =
|
5
|
-
spec.version =
|
6
|
-
spec.authors = [
|
7
|
-
spec.email = [
|
8
|
-
spec.summary =
|
9
|
-
spec.homepage =
|
10
|
-
spec.license =
|
11
|
-
spec.files = [
|
12
|
-
spec.files += Dir.glob(
|
13
|
-
spec.files += Dir.glob(
|
14
|
-
spec.test_files = Dir.glob(
|
4
|
+
spec.name = "ioc"
|
5
|
+
spec.version = "6.0.0"
|
6
|
+
spec.authors = ["Erik Lott"]
|
7
|
+
spec.email = ["erik.lott@kodio.io"]
|
8
|
+
spec.summary = "Minimal dependency injection container for Ruby"
|
9
|
+
spec.homepage = ""
|
10
|
+
spec.license = "MIT"
|
11
|
+
spec.files = ["LICENSE.txt", "README.md", "Rakefile", "ioc.gemspec"]
|
12
|
+
spec.files += Dir.glob("lib/**/*.rb")
|
13
|
+
spec.files += Dir.glob("spec/**/*")
|
14
|
+
spec.test_files = Dir.glob("spec/**/*")
|
15
15
|
spec.require_paths = ["lib"]
|
16
16
|
|
17
17
|
spec.add_development_dependency "bundler", "~> 1.5"
|
18
18
|
spec.add_development_dependency "rake"
|
19
|
+
spec.add_development_dependency "minitest"
|
19
20
|
end
|
data/lib/ioc.rb
CHANGED
@@ -1 +1,37 @@
|
|
1
|
-
|
1
|
+
module IOC
|
2
|
+
class IOCError < ::StandardError; end
|
3
|
+
class DuplicateServiceError < IOCError; end
|
4
|
+
class RegistrationError < IOCError; end
|
5
|
+
class UnknownServiceError < IOCError; end
|
6
|
+
|
7
|
+
class Container
|
8
|
+
def initialize
|
9
|
+
@services = {}
|
10
|
+
@cache = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def register(key, klass=nil, &block)
|
14
|
+
raise(DuplicateServiceError, "Service '#{key}' has already been registered") if @services.key?(key)
|
15
|
+
raise(RegistrationError, "expected class or block") unless klass || block_given?
|
16
|
+
@services[key] = klass ? klass : block
|
17
|
+
end
|
18
|
+
|
19
|
+
def resolve(key)
|
20
|
+
return @cache[key] if @cache.key?(key)
|
21
|
+
raise(UnknownServiceError, "Service '#{key}' not registered") unless @services.key?(key)
|
22
|
+
|
23
|
+
@cache[key] =
|
24
|
+
if @services[key].is_a?(Proc)
|
25
|
+
@services[key].call(self)
|
26
|
+
else
|
27
|
+
dependencies = @services[key].instance_method(:initialize).parameters.map(&:last)
|
28
|
+
resolved_dependencies = dependencies.map{|dependency| resolve(dependency)}
|
29
|
+
@services[key].new(*resolved_dependencies)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def release
|
34
|
+
@cache = {}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/ioc_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "minitest"
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "ioc"
|
4
|
+
|
5
|
+
class CarFixture
|
6
|
+
attr_reader :engine
|
7
|
+
def initialize(engine)
|
8
|
+
@engine = engine
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class EngineFixture; end
|
13
|
+
|
14
|
+
describe IOC do
|
15
|
+
it "can resolve a service class" do
|
16
|
+
container = IOC::Container.new
|
17
|
+
container.register(:myclass, EngineFixture)
|
18
|
+
inst = container.resolve(:myclass)
|
19
|
+
inst.must_be_instance_of(EngineFixture)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can resolve a service classes dependencies" do
|
23
|
+
container = IOC::Container.new
|
24
|
+
container.register(:engine, EngineFixture)
|
25
|
+
container.register(:car, CarFixture)
|
26
|
+
car = container.resolve(:car)
|
27
|
+
car.engine.must_be_instance_of(EngineFixture)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "caches resolved services" do
|
31
|
+
container = IOC::Container.new
|
32
|
+
container.register(:engine, EngineFixture)
|
33
|
+
engine1 = container.resolve(:engine)
|
34
|
+
engine2 = container.resolve(:engine)
|
35
|
+
(engine1 === engine2).must_equal(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can resolve a service block" do
|
39
|
+
myobject = "mystring"
|
40
|
+
container = IOC::Container.new
|
41
|
+
container.register(:myobject){|c| myobject }
|
42
|
+
container.resolve(:myobject).must_equal(myobject)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "complains when resolving unknown service" do
|
46
|
+
container = IOC::Container.new
|
47
|
+
proc {
|
48
|
+
container.resolve(:some_unknown_service)
|
49
|
+
}.must_raise(IOC::UnknownServiceError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "complains when service name registered more than once" do
|
53
|
+
container = IOC::Container.new
|
54
|
+
container.register(:myclass, EngineFixture)
|
55
|
+
proc {
|
56
|
+
container.register(:myclass, EngineFixture)
|
57
|
+
}.must_raise(IOC::DuplicateServiceError)
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ioc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erik Lott
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
description:
|
42
56
|
email:
|
43
57
|
- erik.lott@kodio.io
|
@@ -50,12 +64,7 @@ files:
|
|
50
64
|
- Rakefile
|
51
65
|
- ioc.gemspec
|
52
66
|
- lib/ioc.rb
|
53
|
-
-
|
54
|
-
- lib/ioc/service_block.rb
|
55
|
-
- lib/ioc/service_class.rb
|
56
|
-
- lib/ioc/unresolvable_container.rb
|
57
|
-
- spec/container_spec.rb
|
58
|
-
- spec/spec_helper.rb
|
67
|
+
- spec/ioc_spec.rb
|
59
68
|
homepage: ''
|
60
69
|
licenses:
|
61
70
|
- MIT
|
@@ -76,10 +85,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
85
|
version: '0'
|
77
86
|
requirements: []
|
78
87
|
rubyforge_project:
|
79
|
-
rubygems_version: 2.
|
88
|
+
rubygems_version: 2.4.5
|
80
89
|
signing_key:
|
81
90
|
specification_version: 4
|
82
|
-
summary: Minimal
|
91
|
+
summary: Minimal dependency injection container for Ruby
|
83
92
|
test_files:
|
84
|
-
- spec/
|
85
|
-
- spec/spec_helper.rb
|
93
|
+
- spec/ioc_spec.rb
|
data/lib/ioc/container.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
require "ioc/service_class"
|
2
|
-
require "ioc/service_block"
|
3
|
-
require "ioc/unresolvable_container"
|
4
|
-
|
5
|
-
module IOC
|
6
|
-
class Container
|
7
|
-
def initialize(parent=UnresolvableContainer.new, &block)
|
8
|
-
@services = {}
|
9
|
-
@cache = {}
|
10
|
-
@parent = parent
|
11
|
-
|
12
|
-
self.instance_eval(&block) if block_given?
|
13
|
-
end
|
14
|
-
|
15
|
-
def register(service_name, klass=nil, &block)
|
16
|
-
raise(ArgumentError, "Service '#{service_name}' has already been registered") if @services.key?(service_name)
|
17
|
-
@services[service_name] =
|
18
|
-
if klass
|
19
|
-
ServiceClass.new(klass)
|
20
|
-
elsif block_given?
|
21
|
-
ServiceBlock.new(block)
|
22
|
-
else
|
23
|
-
raise(ArgumentError, "expected service")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def resolve(name)
|
28
|
-
@cache[name] ||= service_locator(name)
|
29
|
-
end
|
30
|
-
|
31
|
-
def [](key)
|
32
|
-
resolve(key)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def service_locator(name)
|
38
|
-
@services.key?(name) ? @services[name].resolve(self) : @parent.resolve(name)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/ioc/service_block.rb
DELETED
data/lib/ioc/service_class.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module IOC
|
2
|
-
class ServiceClass
|
3
|
-
def initialize(klass)
|
4
|
-
klass.instance_method(:initialize)
|
5
|
-
@klass = klass
|
6
|
-
rescue NoMethodError => e
|
7
|
-
raise(ArgumentError, "Expected initialize method on registered service")
|
8
|
-
end
|
9
|
-
|
10
|
-
def resolve(container)
|
11
|
-
dependencies = @klass.instance_method(:initialize).parameters.map(&:last)
|
12
|
-
resolved_dependencies = dependencies.map{ |dependency| container.resolve(dependency) }
|
13
|
-
@klass.new(*resolved_dependencies)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/spec/container_spec.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "ioc"
|
3
|
-
|
4
|
-
describe IOC::Container do
|
5
|
-
context "when class is registered" do
|
6
|
-
it "resolves a class instance" do
|
7
|
-
container = define_container
|
8
|
-
myclass = define_class
|
9
|
-
container.register(:myclass, myclass)
|
10
|
-
inst = container.resolve(:myclass)
|
11
|
-
inst.must_be_instance_of(myclass)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "caches resolved instance" do
|
15
|
-
container = define_container
|
16
|
-
myclass = define_class
|
17
|
-
container.register(:myclass, myclass)
|
18
|
-
inst1 = container.resolve(:myclass)
|
19
|
-
inst2 = container.resolve(:myclass)
|
20
|
-
inst1.must_equal(inst2)
|
21
|
-
end
|
22
|
-
|
23
|
-
context "when class has dependencies" do
|
24
|
-
it "injects dependencies into instance" do
|
25
|
-
container = define_container
|
26
|
-
|
27
|
-
engine_class = define_class
|
28
|
-
container.register(:engine, engine_class)
|
29
|
-
|
30
|
-
car_class = define_class_with_engine_dependency
|
31
|
-
container.register(:car, car_class)
|
32
|
-
|
33
|
-
car = container.resolve(:car)
|
34
|
-
car.engine.must_be_instance_of(engine_class)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "caches dependencies" do
|
38
|
-
container = define_container
|
39
|
-
|
40
|
-
engine_class = define_class
|
41
|
-
container.register(:engine, engine_class)
|
42
|
-
|
43
|
-
car_class = define_class_with_engine_dependency
|
44
|
-
container.register(:car, car_class)
|
45
|
-
|
46
|
-
car = container.resolve(:car)
|
47
|
-
engine = container.resolve(:engine)
|
48
|
-
|
49
|
-
car.engine.must_equal(engine)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context "when block is registered" do
|
55
|
-
it "returns result of block" do
|
56
|
-
myobject = "a string object"
|
57
|
-
container = define_container
|
58
|
-
container.register(:myobject){|c| myobject }
|
59
|
-
container.resolve(:myobject).must_equal(myobject)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
it "raises error when service not found" do
|
64
|
-
container = define_container
|
65
|
-
proc {
|
66
|
-
container.resolve(:some_service)
|
67
|
-
}.must_raise(ArgumentError)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "raises error when service registered more than once" do
|
71
|
-
myclass = define_class
|
72
|
-
container = define_container
|
73
|
-
container.register(:myclass, myclass)
|
74
|
-
proc {
|
75
|
-
container.register(:myclass, myclass)
|
76
|
-
}.must_raise(ArgumentError)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "raises error when object registered as class" do
|
80
|
-
myclass = define_class
|
81
|
-
myobject = myclass.new
|
82
|
-
container = define_container
|
83
|
-
proc {
|
84
|
-
container.register(:myclass, myobject)
|
85
|
-
}.must_raise(ArgumentError)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "supports resolving via hash syntax" do
|
89
|
-
container = define_container
|
90
|
-
myclass = Class.new
|
91
|
-
container.register(:myclass, myclass)
|
92
|
-
inst = container[:myclass]
|
93
|
-
inst.must_be_instance_of(myclass)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "can resolve service from parent container" do
|
97
|
-
myobject = Object.new
|
98
|
-
parent_container = define_container
|
99
|
-
parent_container.register(:myobject){ myobject }
|
100
|
-
|
101
|
-
child_container = define_container(parent_container)
|
102
|
-
child_container.resolve(:myobject).must_equal(myobject)
|
103
|
-
end
|
104
|
-
|
105
|
-
it "can be initialized with a block" do
|
106
|
-
myobject = "a string object"
|
107
|
-
container = define_container do
|
108
|
-
register(:myobject){|c| myobject }
|
109
|
-
end
|
110
|
-
container.resolve(:myobject).must_equal(myobject)
|
111
|
-
end
|
112
|
-
|
113
|
-
def define_container(*args, &block)
|
114
|
-
IOC::Container.new(*args, &block)
|
115
|
-
end
|
116
|
-
|
117
|
-
def define_class_with_engine_dependency
|
118
|
-
Class.new do
|
119
|
-
attr_reader :engine
|
120
|
-
|
121
|
-
def initialize(engine)
|
122
|
-
@engine = engine
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def define_class
|
128
|
-
Class.new
|
129
|
-
end
|
130
|
-
end
|