payload 0.1.0 → 0.2.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/Gemfile.lock +1 -1
- data/README.md +135 -92
- data/lib/payload/container.rb +7 -10
- data/lib/payload/decorator_chain.rb +16 -2
- data/lib/payload/definition.rb +39 -0
- data/lib/payload/definition_list.rb +11 -6
- data/lib/payload/dependency_already_defined_error.rb +5 -0
- data/lib/payload/exported_definition.rb +12 -2
- data/lib/payload/factory.rb +1 -1
- data/lib/payload/factory_resolver.rb +18 -0
- data/lib/payload/service_resolver.rb +17 -0
- data/lib/payload/undefined_definition.rb +37 -0
- data/lib/payload/version.rb +1 -1
- data/spec/payload/container_spec.rb +11 -3
- data/spec/payload/decorator_chain_spec.rb +34 -0
- data/spec/payload/definition_list_spec.rb +78 -27
- data/spec/payload/definition_spec.rb +72 -0
- data/spec/payload/exported_definition_spec.rb +25 -2
- data/spec/payload/factory_resolver_spec.rb +22 -0
- data/spec/payload/service_resolver_spec.rb +23 -0
- data/spec/payload/undefined_definition_spec.rb +65 -0
- metadata +26 -19
- data/lib/payload/factory_definition.rb +0 -24
- data/lib/payload/service_definition.rb +0 -24
- data/spec/payload/factory_definition_spec.rb +0 -49
- data/spec/payload/service_definition_spec.rb +0 -39
@@ -1,5 +1,6 @@
|
|
1
|
+
require 'payload/definition'
|
1
2
|
require 'payload/exported_definition'
|
2
|
-
require 'payload/
|
3
|
+
require 'payload/undefined_definition'
|
3
4
|
|
4
5
|
module Payload
|
5
6
|
# Immutable list of definitions.
|
@@ -12,14 +13,18 @@ module Payload
|
|
12
13
|
@definitions = definitions
|
13
14
|
end
|
14
15
|
|
15
|
-
def add(name,
|
16
|
-
|
16
|
+
def add(name, resolver)
|
17
|
+
value = find(name).set(Definition.new(resolver))
|
18
|
+
self.class.new(definitions.merge(name => value))
|
17
19
|
end
|
18
20
|
|
19
21
|
def find(name)
|
20
|
-
definitions.fetch(name)
|
21
|
-
|
22
|
-
|
22
|
+
definitions.fetch(name) { UndefinedDefinition.new(name) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def decorate(name, block)
|
26
|
+
decorated = find(name).decorate(block)
|
27
|
+
self.class.new(@definitions.merge(name => decorated))
|
23
28
|
end
|
24
29
|
|
25
30
|
def export(names)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Payload
|
2
|
-
# Decorates a base definition such as {
|
2
|
+
# Decorates a base definition such as {ServiceResolver} to provide access to
|
3
3
|
# private dependencies in the {Container} from which the definition was
|
4
4
|
# exported.
|
5
5
|
#
|
@@ -13,7 +13,17 @@ module Payload
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def resolve(container)
|
16
|
-
|
16
|
+
definition.resolve(container.import(private_definitions))
|
17
17
|
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
other.is_a?(ExportedDefinition) &&
|
21
|
+
definition == other.definition &&
|
22
|
+
private_definitions == other.private_definitions
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
attr_reader :definition, :private_definitions
|
18
28
|
end
|
19
29
|
end
|
data/lib/payload/factory.rb
CHANGED
@@ -6,7 +6,7 @@ module Payload
|
|
6
6
|
#
|
7
7
|
# @see Container#factory Container#factory for defining and using factories.
|
8
8
|
class Factory
|
9
|
-
# Used internally by {
|
9
|
+
# Used internally by {FactoryResolver}.
|
10
10
|
#
|
11
11
|
# @api private
|
12
12
|
def initialize(container, block, decorators)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'payload/factory'
|
2
|
+
|
3
|
+
module Payload
|
4
|
+
# Encapsulates logic for resolving factory definitions.
|
5
|
+
#
|
6
|
+
# Used internally by {Container}. Use {Container#factory}.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class FactoryResolver
|
10
|
+
def initialize(block)
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolve(container, decorators)
|
15
|
+
Factory.new(container, @block, decorators)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Payload
|
2
|
+
# Encapsulates logic for resolving service definitions.
|
3
|
+
#
|
4
|
+
# Used internally by {Container}. Use {Container#service}.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class ServiceResolver
|
8
|
+
def initialize(block)
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve(container, decorators)
|
13
|
+
base = @block.call(container)
|
14
|
+
decorators.decorate(base, container)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'payload/decorator_chain'
|
2
|
+
require 'payload/undefined_dependency_error'
|
3
|
+
|
4
|
+
module Payload
|
5
|
+
# Returns from {DefinitionList} when attempting to find an undefined
|
6
|
+
# definition.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class UndefinedDefinition
|
10
|
+
def initialize(name, decorators = DecoratorChain.new)
|
11
|
+
@name = name
|
12
|
+
@decorators = decorators
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
other.is_a?(UndefinedDefinition) && name == other.name
|
17
|
+
end
|
18
|
+
|
19
|
+
def resolve(container)
|
20
|
+
raise UndefinedDependencyError, "No definition for dependency: #{name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def decorate(block)
|
24
|
+
self.class.new(@name, @decorators.add(block))
|
25
|
+
end
|
26
|
+
|
27
|
+
def set(replacement)
|
28
|
+
@decorators.inject(replacement) do |definition, decorator|
|
29
|
+
definition.decorate(decorator)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
attr_reader :name
|
36
|
+
end
|
37
|
+
end
|
data/lib/payload/version.rb
CHANGED
@@ -68,10 +68,18 @@ describe Payload::Container do
|
|
68
68
|
expect(original[:example]).to eq('expected component')
|
69
69
|
end
|
70
70
|
|
71
|
+
it 'decorates a dependency which is defined later' do
|
72
|
+
container = build_container.
|
73
|
+
decorate(:example) { |component, _| "decorated #{component}" }.
|
74
|
+
service(:example) { |_| 'expected component' }
|
75
|
+
|
76
|
+
expect(container[:example]).to eq('decorated expected component')
|
77
|
+
end
|
78
|
+
|
71
79
|
it 'raises an exception for an unknown dependency' do
|
72
80
|
container = build_container
|
73
81
|
|
74
|
-
expect { container.decorate(:
|
82
|
+
expect { container.decorate(:undefined)[:undefined] }
|
75
83
|
.to raise_error(Payload::UndefinedDependencyError)
|
76
84
|
end
|
77
85
|
end
|
@@ -122,9 +130,9 @@ describe Payload::Container do
|
|
122
130
|
describe '#import' do
|
123
131
|
it 'returns a new container with the given definitions' do
|
124
132
|
first_export =
|
125
|
-
Payload::
|
133
|
+
Payload::ServiceResolver.new(lambda { |config| 'one' })
|
126
134
|
second_export =
|
127
|
-
Payload::
|
135
|
+
Payload::ServiceResolver.new(lambda { |config| 'two' })
|
128
136
|
definitions = Payload::DefinitionList.
|
129
137
|
new.
|
130
138
|
add(:one, first_export).
|
@@ -2,6 +2,10 @@ require 'spec_helper'
|
|
2
2
|
require 'payload/decorator_chain'
|
3
3
|
|
4
4
|
describe Payload::DecoratorChain do
|
5
|
+
it 'is enumerable' do
|
6
|
+
expect(Payload::DecoratorChain.new).to be_a(Enumerable)
|
7
|
+
end
|
8
|
+
|
5
9
|
describe '#decorate' do
|
6
10
|
it 'applies a series of decorators to a component' do
|
7
11
|
chain = Payload::DecoratorChain
|
@@ -26,4 +30,34 @@ describe Payload::DecoratorChain do
|
|
26
30
|
expect(result).to eq('Decorated original')
|
27
31
|
end
|
28
32
|
end
|
33
|
+
|
34
|
+
describe '#each' do
|
35
|
+
it 'yields each decorator' do
|
36
|
+
first = double('first')
|
37
|
+
second = double('second')
|
38
|
+
chain = Payload::DecoratorChain.new.add(first).add(second)
|
39
|
+
result = []
|
40
|
+
|
41
|
+
chain.each { |yielded| result << yielded }
|
42
|
+
|
43
|
+
expect(result).to eq([first, second])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#==' do
|
48
|
+
it 'is equal with the same decorators' do
|
49
|
+
expect(Payload::DecoratorChain.new.add(:one).add(:two)).
|
50
|
+
to eq(Payload::DecoratorChain.new.add(:one).add(:two))
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'is unequal with different decorators' do
|
54
|
+
expect(Payload::DecoratorChain.new.add(:one).add(:two)).
|
55
|
+
not_to eq(Payload::DecoratorChain.new.add(:one).add(:three))
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'is unequal with another type' do
|
59
|
+
expect(Payload::DecoratorChain.new.add(:one).add(:two)).
|
60
|
+
not_to eq(:other)
|
61
|
+
end
|
62
|
+
end
|
29
63
|
end
|
@@ -4,22 +4,74 @@ require 'payload/definition_list'
|
|
4
4
|
describe Payload::DefinitionList do
|
5
5
|
describe '#add' do
|
6
6
|
it 'adds a dependency to be found later' do
|
7
|
-
|
7
|
+
resolver = double('resolver')
|
8
|
+
definition = Payload::Definition.new(resolver)
|
8
9
|
definition_list = Payload::DefinitionList.new
|
9
10
|
|
10
|
-
defined = definition_list.add(:example,
|
11
|
+
defined = definition_list.add(:example, resolver)
|
11
12
|
|
12
13
|
expect(defined.find(:example)).to eq(definition)
|
13
14
|
end
|
14
15
|
|
16
|
+
it 'does not replace an existing definition' do
|
17
|
+
definition_list = Payload::DefinitionList.new
|
18
|
+
|
19
|
+
defined = definition_list.add(:example, :original)
|
20
|
+
|
21
|
+
expect { defined.add(:example, :replacement) }.
|
22
|
+
to raise_error(Payload::DependencyAlreadyDefinedError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'does not mutate the list' do
|
26
|
+
resolver = double('resolver')
|
27
|
+
definition_list = Payload::DefinitionList.new
|
28
|
+
|
29
|
+
definition_list.add(:example, resolver)
|
30
|
+
|
31
|
+
expect(definition_list.find(:example)).
|
32
|
+
to eq(Payload::UndefinedDefinition.new(:example))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#decorate' do
|
37
|
+
it 'replaces a dependency with a decorated version' do
|
38
|
+
resolver = double('resolver')
|
39
|
+
decorator = double('decorator')
|
40
|
+
decorated = Payload::Definition.new(resolver).decorate(decorator)
|
41
|
+
definition_list = Payload::DefinitionList.new
|
42
|
+
|
43
|
+
defined = definition_list.
|
44
|
+
add(:example, resolver).
|
45
|
+
decorate(:example, decorator)
|
46
|
+
|
47
|
+
expect(defined.find(:example)).to eq(decorated)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'decorates an undefined dependency' do
|
51
|
+
resolver = double('resolver')
|
52
|
+
decorator = double('decorator')
|
53
|
+
decorated = Payload::Definition.new(resolver).decorate(decorator)
|
54
|
+
definition_list = Payload::DefinitionList.new
|
55
|
+
|
56
|
+
defined = definition_list.
|
57
|
+
decorate(:example, decorator).
|
58
|
+
add(:example, resolver)
|
59
|
+
|
60
|
+
expect(defined.find(:example)).to eq(decorated)
|
61
|
+
end
|
62
|
+
|
15
63
|
it 'does not mutate the list' do
|
16
|
-
|
64
|
+
resolver = double('resolver')
|
65
|
+
decorator = double('decorator')
|
66
|
+
definition = Payload::Definition.new(resolver)
|
17
67
|
definition_list = Payload::DefinitionList.new
|
18
68
|
|
19
|
-
definition_list.
|
69
|
+
defined = definition_list.
|
70
|
+
add(:example, resolver)
|
71
|
+
|
72
|
+
defined.decorate(:example, decorator)
|
20
73
|
|
21
|
-
expect
|
22
|
-
to raise_error(Payload::UndefinedDependencyError)
|
74
|
+
expect(defined.find(:example)).to eq(definition)
|
23
75
|
end
|
24
76
|
end
|
25
77
|
|
@@ -30,23 +82,21 @@ describe Payload::DefinitionList do
|
|
30
82
|
add(:one, 'first').
|
31
83
|
add(:two, 'second').
|
32
84
|
add(:three, 'third')
|
33
|
-
first_exported = double('exported_first')
|
34
|
-
allow(Payload::ExportedDefinition).
|
35
|
-
to receive(:new).
|
36
|
-
with('first', definition_list).
|
37
|
-
and_return(first_exported)
|
38
|
-
second_exported = double('exported_second')
|
39
|
-
allow(Payload::ExportedDefinition).
|
40
|
-
to receive(:new).
|
41
|
-
with('second', definition_list).
|
42
|
-
and_return(second_exported)
|
43
85
|
|
44
86
|
exported = definition_list.export([:one, :two])
|
45
87
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
88
|
+
first = Payload::ExportedDefinition.new(
|
89
|
+
Payload::Definition.new('first'),
|
90
|
+
definition_list
|
91
|
+
)
|
92
|
+
second = Payload::ExportedDefinition.new(
|
93
|
+
Payload::Definition.new('second'),
|
94
|
+
definition_list
|
95
|
+
)
|
96
|
+
third = Payload::UndefinedDefinition.new(:three)
|
97
|
+
expect(exported.find(:one)).to eq(first)
|
98
|
+
expect(exported.find(:two)).to eq(second)
|
99
|
+
expect(exported.find(:three)).to eq(third)
|
50
100
|
end
|
51
101
|
end
|
52
102
|
|
@@ -56,23 +106,24 @@ describe Payload::DefinitionList do
|
|
56
106
|
right = Payload::DefinitionList.new.add(:two, 'second')
|
57
107
|
merged = left.import(right)
|
58
108
|
|
59
|
-
expect(merged.find(:one)).to eq('first')
|
60
|
-
expect(merged.find(:two)).to eq('second')
|
109
|
+
expect(merged.find(:one)).to eq(Payload::Definition.new('first'))
|
110
|
+
expect(merged.find(:two)).to eq(Payload::Definition.new('second'))
|
61
111
|
end
|
62
112
|
end
|
63
113
|
|
64
114
|
describe '#find' do
|
65
|
-
it '
|
115
|
+
it 'returns an unknown definition' do
|
66
116
|
definition_list = Payload::DefinitionList.new
|
67
117
|
|
68
|
-
expect
|
69
|
-
to
|
118
|
+
expect(definition_list.find(:example)).
|
119
|
+
to eq(Payload::UndefinedDefinition.new(:example))
|
70
120
|
end
|
71
121
|
|
72
122
|
it 'returns an existing definition' do
|
73
|
-
|
123
|
+
resolver = double('resolver')
|
124
|
+
definition = Payload::Definition.new(resolver)
|
74
125
|
definition_list =
|
75
|
-
Payload::DefinitionList.new.add(:example,
|
126
|
+
Payload::DefinitionList.new.add(:example, resolver)
|
76
127
|
|
77
128
|
expect(definition_list.find(:example)).to eq(definition)
|
78
129
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/definition'
|
3
|
+
|
4
|
+
describe Payload::Definition do
|
5
|
+
describe '#resolve' do
|
6
|
+
it 'provides the container and decorators to its resolver' do
|
7
|
+
resolved = double('resolved')
|
8
|
+
resolver = double('resolver')
|
9
|
+
container = double('container')
|
10
|
+
decorators = double('decorators')
|
11
|
+
resolver.stub(:resolve).and_return(resolved)
|
12
|
+
definition = Payload::Definition.new(resolver, decorators)
|
13
|
+
|
14
|
+
result = definition.resolve(container)
|
15
|
+
|
16
|
+
expect(result).to eq(resolved)
|
17
|
+
expect(resolver).to have_received(:resolve).with(container, decorators)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#decorate' do
|
22
|
+
it 'returns a new decorated service' do
|
23
|
+
decorator = double('first_decorator')
|
24
|
+
resolver = double('resolver')
|
25
|
+
resolver.stub(:resolve)
|
26
|
+
container = double('container')
|
27
|
+
decorated = double('decorated')
|
28
|
+
decorators = double('decorators')
|
29
|
+
decorators.stub(:add).and_return(decorated)
|
30
|
+
Payload::DecoratorChain.stub(:new).and_return(decorators)
|
31
|
+
definition = Payload::Definition.new(resolver)
|
32
|
+
|
33
|
+
definition.
|
34
|
+
decorate(decorator).
|
35
|
+
resolve(container)
|
36
|
+
|
37
|
+
expect(decorators).to have_received(:add).with(decorator)
|
38
|
+
expect(resolver).to have_received(:resolve).with(container, decorated)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#set' do
|
43
|
+
it 'raises an already defined error' do
|
44
|
+
service = Payload::Definition.new(double('resolver'))
|
45
|
+
|
46
|
+
expect { service.set(double('replacement')) }.
|
47
|
+
to raise_error(Payload::DependencyAlreadyDefinedError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#==' do
|
52
|
+
it 'is true with the same resolver and decorators' do
|
53
|
+
expect(Payload::Definition.new(:resolver).decorate(:decorator)).
|
54
|
+
to eq(Payload::Definition.new(:resolver).decorate(:decorator))
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'is false with a different resolver' do
|
58
|
+
expect(Payload::Definition.new(:resolver).decorate(:decorator)).
|
59
|
+
not_to eq(Payload::Definition.new(:other_resolver).decorate(:decorator))
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'is false with different decorators' do
|
63
|
+
expect(Payload::Definition.new(:resolver).decorate(:decorator)).
|
64
|
+
not_to eq(Payload::Definition.new(:resolver).decorate(:other_decorator))
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'is false with a non-definition' do
|
68
|
+
expect(Payload::Definition.new(:resolver).decorate(:decorator)).
|
69
|
+
not_to eq(:other)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|