ioc 4.1.0 → 6.0.0
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.
- 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
|