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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed5777f12733b8e7e6ed8fc9711354f77b93db58
4
- data.tar.gz: 62796162f3a934ff9668cc39f029b7bb6b58947f
3
+ metadata.gz: c0271481168d1bc9a40d546611f6d3f142eda19a
4
+ data.tar.gz: 530a4e863f7820be1970720975f57136332ddc0c
5
5
  SHA512:
6
- metadata.gz: f5a5251bc60259ea099c2cf2f5402f8dd2b642c8b0ae736305ecfc71e28398ac911b6e68c3e017f9267ab7b1b3da5974f563d3f784ff55496e37fd4ab08fdbd1
7
- data.tar.gz: 1a1a86387be403adfa675b87b9d0b91f3ea401939da9bd8937dddec6c4a34d3cca3948e86f4bc228f0d98e92ab9244869dcc04850542140b4cbb9768203ceb06
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
@@ -2,7 +2,9 @@ require "ruse/injector"
2
2
  require "ruse/version"
3
3
 
4
4
  module Ruse
5
- def self.create_injector
6
- Ruse::Injector.new
5
+ def self.create_injector(config=nil)
6
+ Ruse::Injector.new.tap do |i|
7
+ i.configure(config) if config
8
+ end
7
9
  end
8
10
  end
@@ -1,32 +1,136 @@
1
1
  module Ruse
2
2
  class Injector
3
3
  def get(identifier)
4
- type = resolve_type identifier
5
- args = resolve_dependencies type
6
- type.new *args
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 resolve_type(identifier)
10
- type_name = classify(identifier)
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
- def resolve_dependencies(type)
18
- type.instance_method(:initialize).parameters.map{|_, identifier|
19
- get identifier
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 classify(term)
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 UnknownServiceError < StandardError; end
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
@@ -1,3 +1,3 @@
1
1
  module Ruse
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -3,42 +3,107 @@ require 'minitest/autorun'
3
3
  require 'ruse/injector'
4
4
 
5
5
  describe Ruse::Injector do
6
- it "retrieves an instance it can infer from an identifier" do
7
- injector = Ruse::Injector.new
8
- injector.get("SomeService").must_be_instance_of(SomeService)
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
- injector = Ruse::Injector.new
20
- instance = injector.get("ServiceConsumer")
21
- instance.must_be_instance_of(ServiceConsumer)
22
- instance.service1.must_be_instance_of(SomeService)
23
- instance.service2.must_be_instance_of(OtherService)
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
- class SomeService; end
27
- class OtherService; end
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
- class ServiceConsumer
30
- attr_reader :service1, :service2
31
- def initialize(some_service, other_service)
32
- @service1 = some_service
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::Injector.new
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
@@ -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.1
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-04 00:00:00.000000000 Z
11
+ date: 2014-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler