ruse 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +9 -0
- data/lib/ruse.rb +4 -2
- data/lib/ruse/injector.rb +118 -14
- data/lib/ruse/version.rb +1 -1
- data/specs/injector_spec.rb +83 -18
- data/specs/ruse_spec.rb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0271481168d1bc9a40d546611f6d3f142eda19a
|
4
|
+
data.tar.gz: 530a4e863f7820be1970720975f57136332ddc0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 885a610f5011e855d770749cfb4582115139b6d1a129dc27dba412ee8c0fb6f467fe8e2141bfd1a2549bdec3966cdb1d46a7126f9b83a1363d5f517f07553528
|
7
|
+
data.tar.gz: 251011507aea85f2d8b2417edc9a0b7527167b9db4356ec8d3101b64ad4bb22cd24114eaa39312e3cad6c1072bd228c232bb3d1afc51544e3fd55a73ca30e0d8
|
data/Rakefile
CHANGED
@@ -8,3 +8,12 @@ Rake::TestTask.new do |t|
|
|
8
8
|
t.test_files = FileList['specs/*_spec.rb']
|
9
9
|
t.verbose = true
|
10
10
|
end
|
11
|
+
|
12
|
+
# from http://erniemiller.org/2014/02/05/7-lines-every-gems-rakefile-should-have/
|
13
|
+
task :console do
|
14
|
+
require 'irb'
|
15
|
+
require 'irb/completion'
|
16
|
+
require 'ruse'
|
17
|
+
ARGV.clear
|
18
|
+
IRB.start
|
19
|
+
end
|
data/lib/ruse.rb
CHANGED
data/lib/ruse/injector.rb
CHANGED
@@ -1,32 +1,136 @@
|
|
1
1
|
module Ruse
|
2
2
|
class Injector
|
3
3
|
def get(identifier)
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
identifier = aliases[identifier] || identifier
|
5
|
+
cache_fetch(identifier) do
|
6
|
+
resolver = find_resolver identifier
|
7
|
+
raise UnknownServiceError.new(identifier) unless resolver
|
8
|
+
resolver.build identifier
|
9
|
+
end
|
7
10
|
end
|
8
11
|
|
9
|
-
def
|
10
|
-
|
11
|
-
unless Object.const_defined?(type_name)
|
12
|
-
raise UnknownServiceError.new(type_name)
|
13
|
-
end
|
14
|
-
Object.const_get type_name
|
12
|
+
def configure(settings)
|
13
|
+
configuration.merge! settings
|
15
14
|
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
private
|
17
|
+
|
18
|
+
def cache_fetch(identifier, &block)
|
19
|
+
return cache[identifier] if cache.key?(identifier)
|
20
|
+
cache[identifier] = block.call
|
21
|
+
end
|
22
|
+
|
23
|
+
def cache
|
24
|
+
@cache ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def configuration
|
28
|
+
@configuration ||= {
|
29
|
+
aliases:{},
|
30
|
+
values: {},
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def aliases
|
35
|
+
configuration[:aliases]
|
36
|
+
end
|
37
|
+
|
38
|
+
def values
|
39
|
+
configuration[:values]
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_resolver(identifier)
|
43
|
+
resolvers.detect {|h|
|
44
|
+
h.can_build?(identifier)
|
20
45
|
}
|
21
46
|
end
|
22
47
|
|
23
|
-
def
|
48
|
+
def resolvers
|
49
|
+
@resolvers ||= [
|
50
|
+
ValueResolver.new(values),
|
51
|
+
TypeResolver.new(self),
|
52
|
+
]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class UnknownServiceError < StandardError; end
|
57
|
+
|
58
|
+
class ValueResolver
|
59
|
+
attr_reader :values
|
60
|
+
|
61
|
+
def initialize(values)
|
62
|
+
@values = values
|
63
|
+
end
|
64
|
+
|
65
|
+
def can_build?(identifier)
|
66
|
+
values.key? identifier
|
67
|
+
end
|
68
|
+
|
69
|
+
def build(identifier)
|
70
|
+
values.fetch(identifier)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class TypeResolver
|
75
|
+
def initialize(injector)
|
76
|
+
@injector = injector
|
77
|
+
end
|
78
|
+
|
79
|
+
def can_build?(identifier)
|
80
|
+
type_name = self.class.classify(identifier)
|
81
|
+
load_type type_name
|
82
|
+
end
|
83
|
+
|
84
|
+
def build(identifier)
|
85
|
+
type = resolve_type identifier
|
86
|
+
object_factory.build(type)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.classify(term)
|
24
90
|
# lifted from active_support gem: lib/active_support/inflector/methods.rb
|
25
91
|
string = term.to_s
|
26
92
|
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
27
93
|
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{ $2.capitalize}" }.gsub('/', '::')
|
28
94
|
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def load_type(type_name)
|
99
|
+
type_name.split('::').reduce(Object){|ns, name|
|
100
|
+
if ns.const_defined? name
|
101
|
+
ns.const_get name
|
102
|
+
end
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def object_factory
|
107
|
+
@object_factory ||= ObjectFactory.new(@injector)
|
108
|
+
end
|
109
|
+
|
110
|
+
def resolve_type(identifier)
|
111
|
+
type_name = self.class.classify(identifier)
|
112
|
+
load_type type_name
|
113
|
+
end
|
29
114
|
end
|
30
115
|
|
31
|
-
class
|
116
|
+
class ObjectFactory
|
117
|
+
attr_reader :injector
|
118
|
+
|
119
|
+
def initialize(injector)
|
120
|
+
@injector = injector
|
121
|
+
end
|
122
|
+
|
123
|
+
def build(type)
|
124
|
+
args = resolve_dependencies type
|
125
|
+
type.new *args
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def resolve_dependencies(type)
|
131
|
+
type.instance_method(:initialize).parameters.map{|_, identifier|
|
132
|
+
@injector.get identifier
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
32
136
|
end
|
data/lib/ruse/version.rb
CHANGED
data/specs/injector_spec.rb
CHANGED
@@ -3,42 +3,107 @@ require 'minitest/autorun'
|
|
3
3
|
require 'ruse/injector'
|
4
4
|
|
5
5
|
describe Ruse::Injector do
|
6
|
-
|
7
|
-
injector
|
8
|
-
|
6
|
+
def injector
|
7
|
+
@injector ||= Ruse::Injector.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "retrieves instance when identifier is a class name" do
|
11
|
+
injector.get("ServiceA").must_be_instance_of(ServiceA)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "retrieves instance when identifier can be converted to a class name" do
|
15
|
+
injector.get(:service_a).must_be_instance_of(ServiceA)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "retrieves instance when identifier is namespaced class name" do
|
19
|
+
injector.get("Deep::Namespaced::Service").
|
20
|
+
must_be_instance_of(Deep::Namespaced::Service)
|
9
21
|
end
|
10
22
|
|
11
23
|
it "raises UnknownServiceError for an identifier it cannot resolve" do
|
12
|
-
injector = Ruse::Injector.new
|
13
24
|
->{
|
14
25
|
injector.get("cannot_be_resolved")
|
15
26
|
}.must_raise(Ruse::UnknownServiceError)
|
16
27
|
end
|
17
28
|
|
18
29
|
it "populates dependencies for the instance it retrieves" do
|
19
|
-
|
20
|
-
instance
|
21
|
-
instance.must_be_instance_of(
|
22
|
-
instance.
|
23
|
-
|
30
|
+
instance = injector.get("ConsumerA")
|
31
|
+
instance.must_be_instance_of(ConsumerA)
|
32
|
+
instance.a.must_be_instance_of(ServiceA)
|
33
|
+
instance.b.must_be_instance_of(ServiceB)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "retrieves an instance based on a configured alias" do
|
37
|
+
injector.configure aliases: {special_service: "ServiceA"}
|
38
|
+
injector.get(:special_service).must_be_instance_of(ServiceA)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns same instance of a given service within a single request" do
|
42
|
+
instance = injector.get("ConsumerC")
|
43
|
+
instance.ca.a.must_be_same_as(instance.cb.a)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns same instance of a given service for life of injector" do
|
47
|
+
instance1 = injector.get("ConsumerC")
|
48
|
+
instance2 = injector.get("ConsumerC")
|
49
|
+
instance1.must_be_same_as(instance2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns different instance of given service for different injectors" do
|
53
|
+
instance1 = Ruse::Injector.new.get("ConsumerC")
|
54
|
+
instance2 = Ruse::Injector.new.get("ConsumerC")
|
55
|
+
instance1.wont_be_same_as(instance2)
|
24
56
|
end
|
25
57
|
|
26
|
-
|
27
|
-
|
58
|
+
it "can retrieve a registered value object" do
|
59
|
+
value_object = Object.new
|
60
|
+
injector.configure values: { example: value_object }
|
61
|
+
injector.get(:example).must_be_same_as(value_object)
|
62
|
+
end
|
63
|
+
|
64
|
+
class ServiceA; end
|
65
|
+
class ServiceB; end
|
66
|
+
|
67
|
+
class ConsumerA
|
68
|
+
attr_reader :a, :b
|
69
|
+
def initialize(service_a, service_b)
|
70
|
+
@a = service_a
|
71
|
+
@b = service_b
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ConsumerB
|
76
|
+
attr_reader :a
|
77
|
+
def initialize(service_a)
|
78
|
+
@a = service_a
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class ConsumerC
|
83
|
+
attr_reader :ca, :cb
|
84
|
+
def initialize(consumer_a, consumer_b)
|
85
|
+
@ca = consumer_a
|
86
|
+
@cb = consumer_b
|
87
|
+
end
|
88
|
+
end
|
28
89
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@service2 = other_service
|
90
|
+
module Deep
|
91
|
+
module Namespaced
|
92
|
+
class Service
|
93
|
+
end
|
34
94
|
end
|
35
95
|
end
|
96
|
+
|
36
97
|
end
|
37
98
|
|
38
|
-
# classify will be moved
|
39
99
|
describe "classify" do
|
40
100
|
it "converts an underscored_term to PascalCase" do
|
41
|
-
resolver = Ruse::
|
101
|
+
resolver = Ruse::TypeResolver
|
42
102
|
resolver.classify("camel_case").must_equal("CamelCase")
|
43
103
|
end
|
104
|
+
|
105
|
+
it "echoes back a PascalCase term" do
|
106
|
+
resolver = Ruse::TypeResolver
|
107
|
+
resolver.classify("PascalCase").must_equal("PascalCase")
|
108
|
+
end
|
44
109
|
end
|
data/specs/ruse_spec.rb
CHANGED
@@ -6,4 +6,9 @@ describe Ruse do
|
|
6
6
|
it "can create an injector" do
|
7
7
|
Ruse.create_injector.must_be_kind_of Ruse::Injector
|
8
8
|
end
|
9
|
+
|
10
|
+
it "passes configuration to the injector" do
|
11
|
+
injector = Ruse.create_injector({values: {answer: 42}})
|
12
|
+
injector.get(:answer).must_equal 42
|
13
|
+
end
|
9
14
|
end
|
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.2
|
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-02-
|
11
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|