ruse 0.0.3 → 0.0.4
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/.travis.yml +6 -0
- data/README.md +73 -6
- data/lib/ruse/injector.rb +40 -95
- data/lib/ruse/object_factory.rb +94 -0
- data/lib/ruse/proc_resolver.rb +14 -0
- data/lib/ruse/type_resolver.rb +47 -0
- data/lib/ruse/value_resolver.rb +17 -0
- data/lib/ruse/version.rb +1 -1
- data/ruse.gemspec +1 -1
- data/specs/injector_keyword_args_spec.rb +75 -0
- data/specs/injector_spec.rb +48 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d095e3c4fbabc172f422463a08a3509906cd45cd
|
4
|
+
data.tar.gz: 511b955cccf7a731dec5b94f2fdda97ad6bdd6a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c12916ebcd87eb57c84f6ab9f965323942fea70b3fbc49b69c184d3e7b5af957312908dedc8f0d4914391593ebe6c9be20745298d057381e22b15de0c0876b7
|
7
|
+
data.tar.gz: 37ffdd78470a69c001b778688a94dabec4d6229c28306a7c977107b4cab8380eba569347f83d23f1bc9e7ad06d3085dff9d5b6f12a3d0e63a5dfc540c0cd9863
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -82,19 +82,86 @@ all the way down the object graph.
|
|
82
82
|
|
83
83
|
## Instance Resolution
|
84
84
|
|
85
|
-
Dependencies are determined by the identifiers used in
|
85
|
+
Dependencies are determined by the identifiers used in constructor parameters.
|
86
86
|
This was lifted directly from angular.js, and I believe may be the key to
|
87
87
|
reducing the overhead in using a tool like this. Your dependency consuming
|
88
88
|
classes do not have to be annotated or registered in any way.
|
89
89
|
|
90
|
-
|
90
|
+
By default, identifiers are resolved to types through simple
|
91
91
|
string manipulation (similiar to ActiveSupport's `classify` and `constantize`).
|
92
92
|
That means you can get an instance of `SomeService` by requesting
|
93
|
-
`"SomeService"`, `"some_service"` or `:some_service`.
|
93
|
+
`"SomeService"`, `"some_service"` or `:some_service`. The type is then
|
94
|
+
instantiated (populating all of *its* dependencies using the same mechanism)
|
95
|
+
and passed in to the constructor.
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
However, you can configure the injector to use types that differ from the
|
98
|
+
parameter name, or return existing objects.
|
99
|
+
|
100
|
+
## Instance Lifecycle
|
101
|
+
|
102
|
+
Currently, all objects retrieved from the injector are treated as singletons.
|
103
|
+
This means that any time you ask a given injector for an instance of a
|
104
|
+
service, you will always get the same exact instance. If you want a new
|
105
|
+
instance, you need to use a new instance of the injector. In the future,
|
106
|
+
the lifecycle may be configurable, but I haven't needed it yet.
|
107
|
+
|
108
|
+
## Configuration
|
109
|
+
|
110
|
+
Currently, you configure the injector by passing an options `Hash` to
|
111
|
+
`Ruse.create_injector` or to the `#configure` method of an `Injector` instance.
|
112
|
+
You can call the `#configure` method multiple times, and each time the options
|
113
|
+
will be merged into the existing options.
|
114
|
+
|
115
|
+
Eventually there may be an API or DSL for building the options `Hash`, but for
|
116
|
+
now, you need to know the specific keys that are understood internally.
|
117
|
+
|
118
|
+
### Aliases
|
119
|
+
|
120
|
+
Aliases are the most common configuration. They allow you to specify the type
|
121
|
+
that should be injected for a given parameter name. In the example above,
|
122
|
+
the `CreateOrderCommand` relies on a `:notifier`. By default, Ruse will
|
123
|
+
attempt to inject an instance of `Notifier`. However, you may have two services
|
124
|
+
that can act as a `notifier`: `EmailNotifier` or `FileSystemNotifier`.
|
125
|
+
|
126
|
+
In production, you want real emails to be sent, so you would configure the
|
127
|
+
injector to use `EmailNotifier`:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
Ruse.create_injector aliases: {notifier: "EmailNotifier"}
|
131
|
+
```
|
132
|
+
|
133
|
+
However, in testing, you just want to record notifications in the filesystem:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
Ruse.create_injector aliases: {notifier: "FileSystemNotifier"}
|
137
|
+
```
|
138
|
+
|
139
|
+
### Values
|
140
|
+
|
141
|
+
Sometimes you want to inject a specific value, or existing object instance,
|
142
|
+
instead of relying on the injector to create the instance.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
Ruse.create_injector values: {max_uploads: 42, file_system: File}
|
146
|
+
```
|
147
|
+
|
148
|
+
You could now create a class that depends on `file_system` and send messages
|
149
|
+
exposed by `File`, without an explicit dependency on `File` (making it easy
|
150
|
+
to pass in a stub file system during tests).
|
151
|
+
|
152
|
+
### Factories / Procs / Delayed Evaluation
|
153
|
+
|
154
|
+
Factories are similar to `values`, but allow you to delay the creation of the
|
155
|
+
object until it is requested. For example, you might want to inject a
|
156
|
+
connection to a 3rd party service, but you don't want to create the connection
|
157
|
+
at configuration time - you want to wait until it is used.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
Ruse.create_injector factories: {
|
161
|
+
s3_connection:
|
162
|
+
->{ AWS::S3.new(config).buckets["my_files"] }
|
163
|
+
}
|
164
|
+
```
|
98
165
|
|
99
166
|
## Installation
|
100
167
|
|
data/lib/ruse/injector.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
require 'ruse/proc_resolver'
|
2
|
+
require 'ruse/type_resolver'
|
3
|
+
require 'ruse/value_resolver'
|
4
|
+
require 'ruse/object_factory'
|
5
|
+
|
1
6
|
module Ruse
|
2
7
|
class Injector
|
3
8
|
def get(identifier)
|
9
|
+
ensure_valid_identifier! identifier
|
4
10
|
identifier = aliases[identifier] || identifier
|
5
11
|
cache_fetch(identifier) do
|
6
12
|
resolver = find_resolver identifier
|
@@ -10,11 +16,32 @@ module Ruse
|
|
10
16
|
end
|
11
17
|
|
12
18
|
def configure(settings)
|
13
|
-
configuration.
|
19
|
+
configuration.each do |key, hsh|
|
20
|
+
hsh.merge!(settings[key] || {})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def can_resolve?(identifier)
|
25
|
+
return false if invalid_identifier?(identifier)
|
26
|
+
find_resolver(identifier) ? true : false
|
14
27
|
end
|
15
28
|
|
16
29
|
private
|
17
30
|
|
31
|
+
def ensure_valid_identifier!(identifier)
|
32
|
+
if invalid_identifier? identifier
|
33
|
+
raise InvalidServiceName.new("<#{identifier.inspect}>")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def invalid_identifier?(identifier)
|
38
|
+
identifier.nil? || empty_string?(identifier)
|
39
|
+
end
|
40
|
+
|
41
|
+
def empty_string?(s)
|
42
|
+
s.is_a?(String) && s !~ /[^[:space:]]/
|
43
|
+
end
|
44
|
+
|
18
45
|
def cache_fetch(identifier, &block)
|
19
46
|
return cache[identifier] if cache.key?(identifier)
|
20
47
|
cache[identifier] = block.call
|
@@ -32,6 +59,17 @@ module Ruse
|
|
32
59
|
}
|
33
60
|
end
|
34
61
|
|
62
|
+
def reset_configuration
|
63
|
+
@configuration = nil
|
64
|
+
end
|
65
|
+
# Allow #initialize_clone to reset the configuration of the cloned injector
|
66
|
+
protected :reset_configuration
|
67
|
+
|
68
|
+
def initialize_clone(cloned_injector)
|
69
|
+
cloned_injector.reset_configuration
|
70
|
+
cloned_injector.configure(configuration)
|
71
|
+
end
|
72
|
+
|
35
73
|
def aliases
|
36
74
|
configuration[:aliases]
|
37
75
|
end
|
@@ -60,98 +98,5 @@ module Ruse
|
|
60
98
|
end
|
61
99
|
|
62
100
|
class UnknownServiceError < StandardError; end
|
63
|
-
|
64
|
-
class ProcResolver
|
65
|
-
attr_reader :factories
|
66
|
-
def initialize(factories)
|
67
|
-
@factories = factories
|
68
|
-
end
|
69
|
-
def can_build?(identifier)
|
70
|
-
factories.key? identifier
|
71
|
-
end
|
72
|
-
|
73
|
-
def build(identifier)
|
74
|
-
factory = factories.fetch(identifier)
|
75
|
-
factory.call
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
class ValueResolver
|
80
|
-
attr_reader :values
|
81
|
-
|
82
|
-
def initialize(values)
|
83
|
-
@values = values
|
84
|
-
end
|
85
|
-
|
86
|
-
def can_build?(identifier)
|
87
|
-
values.key? identifier
|
88
|
-
end
|
89
|
-
|
90
|
-
def build(identifier)
|
91
|
-
values.fetch(identifier)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
class TypeResolver
|
96
|
-
def initialize(injector)
|
97
|
-
@injector = injector
|
98
|
-
end
|
99
|
-
|
100
|
-
def can_build?(identifier)
|
101
|
-
type_name = self.class.classify(identifier)
|
102
|
-
load_type type_name
|
103
|
-
end
|
104
|
-
|
105
|
-
def build(identifier)
|
106
|
-
type = resolve_type identifier
|
107
|
-
object_factory.build(type)
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.classify(term)
|
111
|
-
# lifted from active_support gem: lib/active_support/inflector/methods.rb
|
112
|
-
string = term.to_s
|
113
|
-
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
114
|
-
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{ $2.capitalize}" }.gsub('/', '::')
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
def load_type(type_name)
|
120
|
-
type_name.split('::').reduce(Object){|ns, name|
|
121
|
-
if ns.const_defined? name
|
122
|
-
ns.const_get name
|
123
|
-
end
|
124
|
-
}
|
125
|
-
end
|
126
|
-
|
127
|
-
def object_factory
|
128
|
-
@object_factory ||= ObjectFactory.new(@injector)
|
129
|
-
end
|
130
|
-
|
131
|
-
def resolve_type(identifier)
|
132
|
-
type_name = self.class.classify(identifier)
|
133
|
-
load_type type_name
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
class ObjectFactory
|
138
|
-
attr_reader :injector
|
139
|
-
|
140
|
-
def initialize(injector)
|
141
|
-
@injector = injector
|
142
|
-
end
|
143
|
-
|
144
|
-
def build(type)
|
145
|
-
args = resolve_dependencies type
|
146
|
-
type.new *args
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
def resolve_dependencies(type)
|
152
|
-
type.instance_method(:initialize).parameters.map{|_, identifier|
|
153
|
-
@injector.get identifier
|
154
|
-
}
|
155
|
-
end
|
156
|
-
end
|
101
|
+
class InvalidServiceName < StandardError; end
|
157
102
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Ruse
|
2
|
+
class ObjectFactory
|
3
|
+
attr_reader :injector
|
4
|
+
|
5
|
+
def initialize(injector)
|
6
|
+
@injector = injector
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(type)
|
10
|
+
initializer = Initializer.new @injector, type.instance_method(:initialize)
|
11
|
+
initializer.resolve_dependencies!
|
12
|
+
type.new *initializer.args
|
13
|
+
end
|
14
|
+
|
15
|
+
class Initializer
|
16
|
+
attr_reader :positional_args, :keyword_args
|
17
|
+
|
18
|
+
def initialize(injector, initialize_method)
|
19
|
+
@injector = injector
|
20
|
+
@initialize_method = initialize_method
|
21
|
+
@positional_args = []
|
22
|
+
@keyword_args = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def args
|
26
|
+
[*positional_args, keyword_args].tap do |list|
|
27
|
+
list.pop if list.last.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def resolve_dependencies!
|
32
|
+
@initialize_method.parameters.each do |arg_type, identifier|
|
33
|
+
MethodArgument.build(arg_type, identifier, @injector).resolve(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
MethodArgument = Struct.new :arg_type, :identifier, :injector do
|
38
|
+
def self.build(arg_type, *args)
|
39
|
+
[PositionalArgument, KeywordArgument].each do |klass|
|
40
|
+
return klass.new(arg_type, *args) if klass.match?(arg_type)
|
41
|
+
end
|
42
|
+
UnhandleableArgument
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_dependency
|
46
|
+
injector.get identifier
|
47
|
+
end
|
48
|
+
|
49
|
+
def must_resolve?
|
50
|
+
required? || injector.can_resolve?(identifier)
|
51
|
+
end
|
52
|
+
|
53
|
+
def resolve(initializer)
|
54
|
+
resolve!(initializer) if must_resolve?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class PositionalArgument < MethodArgument
|
59
|
+
def self.match?(arg_type)
|
60
|
+
[:req, :opt].include? arg_type
|
61
|
+
end
|
62
|
+
|
63
|
+
def required?
|
64
|
+
arg_type == :req
|
65
|
+
end
|
66
|
+
|
67
|
+
def resolve!(initializer)
|
68
|
+
initializer.positional_args << build_dependency
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class KeywordArgument < MethodArgument
|
73
|
+
def self.match?(arg_type)
|
74
|
+
[:key, :keyreq].include? arg_type
|
75
|
+
end
|
76
|
+
|
77
|
+
def required?
|
78
|
+
arg_type == :keyreq
|
79
|
+
end
|
80
|
+
|
81
|
+
def resolve!(initializer)
|
82
|
+
initializer.keyword_args[identifier] = build_dependency
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Singleton object that be substituted for the other MethodArgument
|
87
|
+
# subclasses when we don't want to try resolving the argument.
|
88
|
+
module UnhandleableArgument
|
89
|
+
extend self
|
90
|
+
def resolve(*) end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class ProcResolver
|
2
|
+
attr_reader :factories
|
3
|
+
def initialize(factories)
|
4
|
+
@factories = factories
|
5
|
+
end
|
6
|
+
def can_build?(identifier)
|
7
|
+
factories.key? identifier
|
8
|
+
end
|
9
|
+
|
10
|
+
def build(identifier)
|
11
|
+
factory = factories.fetch(identifier)
|
12
|
+
factory.call
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ruse
|
2
|
+
class TypeResolver
|
3
|
+
def initialize(injector)
|
4
|
+
@injector = injector
|
5
|
+
end
|
6
|
+
|
7
|
+
def can_build?(identifier)
|
8
|
+
type_name = self.class.classify(identifier)
|
9
|
+
load_type type_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def build(identifier)
|
13
|
+
type = resolve_type identifier
|
14
|
+
object_factory.build(type)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.classify(term)
|
18
|
+
# lifted from active_support gem: lib/active_support/inflector/methods.rb
|
19
|
+
string = term.to_s
|
20
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
21
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{ $2.capitalize}" }.gsub('/', '::')
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def base_module
|
27
|
+
Object
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_type(type_name)
|
31
|
+
type_name.split('::').reduce(base_module){|ns, name|
|
32
|
+
if ns.const_defined? name
|
33
|
+
ns.const_get name
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def object_factory
|
39
|
+
@object_factory ||= ObjectFactory.new(@injector)
|
40
|
+
end
|
41
|
+
|
42
|
+
def resolve_type(identifier)
|
43
|
+
type_name = self.class.classify(identifier)
|
44
|
+
load_type type_name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ruse
|
2
|
+
class ValueResolver
|
3
|
+
attr_reader :values
|
4
|
+
|
5
|
+
def initialize(values)
|
6
|
+
@values = values
|
7
|
+
end
|
8
|
+
|
9
|
+
def can_build?(identifier)
|
10
|
+
values.key? identifier
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(identifier)
|
14
|
+
values.fetch(identifier)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/ruse/version.rb
CHANGED
data/ruse.gemspec
CHANGED
@@ -18,6 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency "bundler", "~> 1.
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'ruse/injector'
|
4
|
+
|
5
|
+
describe Ruse::Injector do
|
6
|
+
def injector
|
7
|
+
@injector ||= Ruse::Injector.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "injects keyword arguments it can resolve, delegating to defaults when it can't" do
|
11
|
+
skip("No keyword argument support before Ruby 2.0") unless RUBY_VERSION >= "2.0"
|
12
|
+
object = injector.get("HasKeywordArguments")
|
13
|
+
object.a.must_be_kind_of ServiceA
|
14
|
+
object.z.must_equal :z
|
15
|
+
end
|
16
|
+
|
17
|
+
it "injects optional parameters it can resolve, delegating to defaults when it can't" do
|
18
|
+
skip("No keyword argument support before Ruby 2.0") unless RUBY_VERSION >= "2.0"
|
19
|
+
object = injector.get("HasOptionalParameters")
|
20
|
+
object.a.must_be_kind_of ServiceA
|
21
|
+
object.z.must_equal :z
|
22
|
+
end
|
23
|
+
|
24
|
+
it "injects required keyword arguments" do
|
25
|
+
skip("No required keyword argument support before Ruby 2.1") unless RUBY_VERSION >= "2.1"
|
26
|
+
object = injector.get("HasRequiredKeywordArguments")
|
27
|
+
object.a.must_be_kind_of ServiceA
|
28
|
+
end
|
29
|
+
|
30
|
+
it "exceptions when a required keyword argument can't resolve" do
|
31
|
+
skip("No required keyword argument support before Ruby 2.1") unless RUBY_VERSION >= "2.1"
|
32
|
+
-> {
|
33
|
+
injector.get("HasUnresolvableRequiredKeywordArguments")
|
34
|
+
}.must_raise Ruse::UnknownServiceError
|
35
|
+
end
|
36
|
+
|
37
|
+
class ServiceA; end
|
38
|
+
|
39
|
+
if RUBY_VERSION >= "2.0"
|
40
|
+
class HasOptionalParameters
|
41
|
+
attr_reader :a, :z
|
42
|
+
class_eval <<-EVAL, __FILE__, __LINE__
|
43
|
+
def initialize(service_a = :a, service_z = :z)
|
44
|
+
@a = service_a
|
45
|
+
@z = service_z
|
46
|
+
end
|
47
|
+
EVAL
|
48
|
+
end
|
49
|
+
|
50
|
+
class HasKeywordArguments
|
51
|
+
attr_reader :a, :z
|
52
|
+
class_eval <<-EVAL, __FILE__, __LINE__
|
53
|
+
def initialize(service_a: :a, service_z: :z)
|
54
|
+
@a = service_a
|
55
|
+
@z = service_z
|
56
|
+
end
|
57
|
+
EVAL
|
58
|
+
end
|
59
|
+
end
|
60
|
+
if RUBY_VERSION >= "2.1"
|
61
|
+
class HasRequiredKeywordArguments
|
62
|
+
attr_reader :a
|
63
|
+
class_eval <<-EVAL, __FILE__, __LINE__
|
64
|
+
def initialize(service_a:)
|
65
|
+
@a = service_a
|
66
|
+
end
|
67
|
+
EVAL
|
68
|
+
end
|
69
|
+
class HasUnresolvableRequiredKeywordArguments
|
70
|
+
class_eval <<-EVAL, __FILE__, __LINE__
|
71
|
+
def initialize(service_z:) end
|
72
|
+
EVAL
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/specs/injector_spec.rb
CHANGED
@@ -26,6 +26,27 @@ describe Ruse::Injector do
|
|
26
26
|
}.must_raise(Ruse::UnknownServiceError)
|
27
27
|
end
|
28
28
|
|
29
|
+
it "raises InvalidServiceName when identifier is nil" do
|
30
|
+
->{
|
31
|
+
injector.get(nil)
|
32
|
+
}.must_raise(Ruse::InvalidServiceName)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "raises InvalidServiceName when identifier is blank string" do
|
36
|
+
->{
|
37
|
+
injector.get(" ")
|
38
|
+
}.must_raise(Ruse::InvalidServiceName)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "cannot resolve a nil identifier" do
|
42
|
+
refute injector.can_resolve?(nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "cannot resolve a blank string identifier" do
|
46
|
+
refute injector.can_resolve?(" ")
|
47
|
+
end
|
48
|
+
|
49
|
+
|
29
50
|
it "populates dependencies for the instance it retrieves" do
|
30
51
|
instance = injector.get("ConsumerA")
|
31
52
|
instance.must_be_instance_of(ConsumerA)
|
@@ -70,6 +91,29 @@ describe Ruse::Injector do
|
|
70
91
|
instance.value.must_equal(88)
|
71
92
|
end
|
72
93
|
|
94
|
+
it "skips over splats" do
|
95
|
+
injector.get("HasSplatInInitializer").must_be_kind_of HasSplatInInitializer
|
96
|
+
injector.get("Array").must_equal []
|
97
|
+
end
|
98
|
+
|
99
|
+
it "can be reconfigured" do
|
100
|
+
injector.configure(values: { service_a: 'foo' })
|
101
|
+
injector.configure(values: { service_b: 'bar' })
|
102
|
+
object = injector.get(:consumer_a)
|
103
|
+
object.a.must_equal 'foo'
|
104
|
+
object.b.must_equal 'bar'
|
105
|
+
end
|
106
|
+
|
107
|
+
it "duplicates configuration on cloning" do
|
108
|
+
injector.configure(values: { service_a: 'foo', service_b: 'bar' })
|
109
|
+
child_injector = injector.clone
|
110
|
+
child_injector.configure(values: { service_a: 'FOO' })
|
111
|
+
child_object = child_injector.get(:consumer_a)
|
112
|
+
parent_object = injector.get(:consumer_a)
|
113
|
+
|
114
|
+
child_object.a.must_equal 'FOO'
|
115
|
+
parent_object.a.must_equal 'foo'
|
116
|
+
end
|
73
117
|
|
74
118
|
class ServiceA; end
|
75
119
|
class ServiceB; end
|
@@ -111,6 +155,10 @@ describe Ruse::Injector do
|
|
111
155
|
end
|
112
156
|
end
|
113
157
|
|
158
|
+
class HasSplatInInitializer
|
159
|
+
def initialize(*args)
|
160
|
+
end
|
161
|
+
end
|
114
162
|
end
|
115
163
|
|
116
164
|
describe "classify" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Flanagan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -17,13 +17,13 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '1.
|
20
|
+
version: '1.5'
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
type: :development
|
@@ -46,6 +46,7 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- .gitignore
|
49
|
+
- .travis.yml
|
49
50
|
- Gemfile
|
50
51
|
- LICENSE.txt
|
51
52
|
- README.md
|
@@ -53,8 +54,13 @@ files:
|
|
53
54
|
- lib/ruse.rb
|
54
55
|
- lib/ruse/activesupport.rb
|
55
56
|
- lib/ruse/injector.rb
|
57
|
+
- lib/ruse/object_factory.rb
|
58
|
+
- lib/ruse/proc_resolver.rb
|
59
|
+
- lib/ruse/type_resolver.rb
|
60
|
+
- lib/ruse/value_resolver.rb
|
56
61
|
- lib/ruse/version.rb
|
57
62
|
- ruse.gemspec
|
63
|
+
- specs/injector_keyword_args_spec.rb
|
58
64
|
- specs/injector_spec.rb
|
59
65
|
- specs/ruse_spec.rb
|
60
66
|
homepage: https://github.com/joshuaflanagan/ruse
|