ruse 0.0.1 → 0.0.2
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/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
|