payload 0.1.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 +7 -0
- data/.gitignore +4 -0
- data/CONTRIBUTING.md +10 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/LICENSE +19 -0
- data/README.md +242 -0
- data/Rakefile +10 -0
- data/lib/payload/container.rb +96 -0
- data/lib/payload/controller.rb +12 -0
- data/lib/payload/decorator_chain.rb +23 -0
- data/lib/payload/definition_list.rb +41 -0
- data/lib/payload/exported_definition.rb +19 -0
- data/lib/payload/factory.rb +33 -0
- data/lib/payload/factory_definition.rb +24 -0
- data/lib/payload/mutable_container.rb +57 -0
- data/lib/payload/rack_container.rb +23 -0
- data/lib/payload/rails_loader.rb +58 -0
- data/lib/payload/railtie.rb +20 -0
- data/lib/payload/service_definition.rb +24 -0
- data/lib/payload/testing.rb +84 -0
- data/lib/payload/undefined_dependency_error.rb +5 -0
- data/lib/payload/version.rb +3 -0
- data/payload.gemspec +24 -0
- data/spec/payload/container_spec.rb +154 -0
- data/spec/payload/controller_spec.rb +32 -0
- data/spec/payload/decorator_chain_spec.rb +29 -0
- data/spec/payload/definition_list_spec.rb +80 -0
- data/spec/payload/exported_definition_spec.rb +30 -0
- data/spec/payload/factory_definition_spec.rb +49 -0
- data/spec/payload/factory_spec.rb +46 -0
- data/spec/payload/mutable_container_spec.rb +68 -0
- data/spec/payload/rack_container_spec.rb +33 -0
- data/spec/payload/rails_loader_spec.rb +62 -0
- data/spec/payload/service_definition_spec.rb +39 -0
- data/spec/spec_helper.rb +0 -0
- metadata +137 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/container'
|
3
|
+
require 'payload/testing'
|
4
|
+
|
5
|
+
describe Payload::Container do
|
6
|
+
include Payload::Testing
|
7
|
+
|
8
|
+
describe '#factory' do
|
9
|
+
it 'returns an object that responds to new' do
|
10
|
+
container = build_container
|
11
|
+
.service(:from_container) { |config| 'From container' }
|
12
|
+
.factory(:example) do |config|
|
13
|
+
"#{config[:from_new]} and #{config[:from_container]}"
|
14
|
+
end
|
15
|
+
|
16
|
+
result = container[:example].new(from_new: 'From new')
|
17
|
+
|
18
|
+
expect(result).to eq('From new and From container')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#service' do
|
23
|
+
it 'returns a container with the given dependency defined' do
|
24
|
+
container = build_container
|
25
|
+
.service(:example) { |config| 'expected result' }
|
26
|
+
|
27
|
+
expect(container[:example]).to eq('expected result')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "doesn't mutate the container" do
|
31
|
+
original = build_container
|
32
|
+
original.service(:example) { |config| 'expected result' }
|
33
|
+
|
34
|
+
expect { original[:example] }
|
35
|
+
.to raise_error(Payload::UndefinedDependencyError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'provides access to other dependencies' do
|
39
|
+
container = build_container
|
40
|
+
.service(:example) { |config| "got #{config[:dependency].inspect}" }
|
41
|
+
.service(:dependency) { |config| 'expected result' }
|
42
|
+
|
43
|
+
expect(container[:example]).to eq('got "expected result"')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#decorate' do
|
48
|
+
it 'returns a container with the given dependency decorated' do
|
49
|
+
container = build_container
|
50
|
+
.service(:example) { |config| 'expected component' }
|
51
|
+
.decorate(:example) do |component, config|
|
52
|
+
"decorated #{component.inspect} with #{config[:param].inspect}"
|
53
|
+
end
|
54
|
+
.decorate(:example) { |component, config| "redecorated #{component}" }
|
55
|
+
.service(:param) { |config| 'expected param' }
|
56
|
+
|
57
|
+
expect(container[:example]).to eq(
|
58
|
+
'redecorated decorated "expected component" with "expected param"'
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't mutate the container" do
|
63
|
+
original = build_container
|
64
|
+
.service(:example) { |config| 'expected component' }
|
65
|
+
original
|
66
|
+
.decorate(:example) { |component, config| "decorated #{component}" }
|
67
|
+
|
68
|
+
expect(original[:example]).to eq('expected component')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'raises an exception for an unknown dependency' do
|
72
|
+
container = build_container
|
73
|
+
|
74
|
+
expect { container.decorate(:anything) }
|
75
|
+
.to raise_error(Payload::UndefinedDependencyError)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#export' do
|
80
|
+
it 'allows access to exported definitions' do
|
81
|
+
exports = build_container.
|
82
|
+
service(:example) { |config| 'expected component' }.
|
83
|
+
export(:example)
|
84
|
+
container = build_container.import(exports)
|
85
|
+
|
86
|
+
expect(container[:example]).to eq('expected component')
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'does not allow access to private definitions' do
|
90
|
+
exports = build_container.
|
91
|
+
service(:exported) { |config| 'exported' }.
|
92
|
+
service(:private) { |config| 'private' }.
|
93
|
+
export(:exported)
|
94
|
+
container = build_container.import(exports)
|
95
|
+
|
96
|
+
expect { container[:private] }.
|
97
|
+
to raise_error(Payload::UndefinedDependencyError)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'allows exported definitions to reference private definitions' do
|
101
|
+
exports = build_container.
|
102
|
+
service(:exported) { |config| "got #{config[:private].inspect}" }.
|
103
|
+
service(:private) { |config| 'private' }.
|
104
|
+
export(:exported)
|
105
|
+
container = build_container.import(exports)
|
106
|
+
|
107
|
+
expect(container[:exported]).to eq('got "private"')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'exposes local definitions to private definitions' do
|
111
|
+
exports = build_container.
|
112
|
+
service(:exported) { |config| "got #{config[:local].inspect}" }.
|
113
|
+
export(:exported)
|
114
|
+
container = build_container.
|
115
|
+
service(:local) { |config| 'local' }.
|
116
|
+
import(exports)
|
117
|
+
|
118
|
+
expect(container[:exported]).to eq('got "local"')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#import' do
|
123
|
+
it 'returns a new container with the given definitions' do
|
124
|
+
first_export =
|
125
|
+
Payload::ServiceDefinition.new(lambda { |config| 'one' })
|
126
|
+
second_export =
|
127
|
+
Payload::ServiceDefinition.new(lambda { |config| 'two' })
|
128
|
+
definitions = Payload::DefinitionList.
|
129
|
+
new.
|
130
|
+
add(:one, first_export).
|
131
|
+
add(:two, second_export)
|
132
|
+
container = build_container.
|
133
|
+
service(:original) { |config| 'original' }.
|
134
|
+
import(definitions)
|
135
|
+
|
136
|
+
expect(container[:original]).to eq('original')
|
137
|
+
expect(container[:one]).to eq('one')
|
138
|
+
expect(container[:two]).to eq('two')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '#[]' do
|
143
|
+
context 'with an undefined dependency' do
|
144
|
+
it 'raises an undefined dependency error' do
|
145
|
+
container = build_container
|
146
|
+
|
147
|
+
expect { container[:undefined] }.to raise_error(
|
148
|
+
Payload::UndefinedDependencyError,
|
149
|
+
'No definition for dependency: undefined'
|
150
|
+
)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/controller'
|
3
|
+
|
4
|
+
describe Payload::Controller do
|
5
|
+
describe '#dependencies' do
|
6
|
+
it 'looks for dependencies from the Rack environment' do
|
7
|
+
dependency = double('dependency')
|
8
|
+
container = { lookup: dependency }
|
9
|
+
env = { dependencies: container }
|
10
|
+
request = double('request', env: env)
|
11
|
+
controller_class = Class.new do
|
12
|
+
include Payload::Controller
|
13
|
+
|
14
|
+
def initialize(request)
|
15
|
+
@request = request
|
16
|
+
end
|
17
|
+
|
18
|
+
def lookup
|
19
|
+
dependencies[:lookup]
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :request
|
25
|
+
end
|
26
|
+
|
27
|
+
result = controller_class.new(request).lookup
|
28
|
+
|
29
|
+
expect(result).to eq(dependency)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/decorator_chain'
|
3
|
+
|
4
|
+
describe Payload::DecoratorChain do
|
5
|
+
describe '#decorate' do
|
6
|
+
it 'applies a series of decorators to a component' do
|
7
|
+
chain = Payload::DecoratorChain
|
8
|
+
.new
|
9
|
+
.add(lambda { |base, config| "Decorated #{base} with #{config[:one]}" })
|
10
|
+
.add(lambda { |base, config| "#{base} and #{config[:two]}" })
|
11
|
+
|
12
|
+
result = chain.decorate('original', one: 'one', two: 'two')
|
13
|
+
|
14
|
+
expect(result).to eq('Decorated original with one and two')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "doesn't mutate" do
|
18
|
+
chain = Payload::DecoratorChain
|
19
|
+
.new
|
20
|
+
.add(lambda { |base, config| "Decorated #{base}" })
|
21
|
+
|
22
|
+
chain.add(lambda { |base, config| 'Unreferenced decorator' })
|
23
|
+
|
24
|
+
result = chain.decorate('original', {})
|
25
|
+
|
26
|
+
expect(result).to eq('Decorated original')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/definition_list'
|
3
|
+
|
4
|
+
describe Payload::DefinitionList do
|
5
|
+
describe '#add' do
|
6
|
+
it 'adds a dependency to be found later' do
|
7
|
+
definition = double('definition')
|
8
|
+
definition_list = Payload::DefinitionList.new
|
9
|
+
|
10
|
+
defined = definition_list.add(:example, definition)
|
11
|
+
|
12
|
+
expect(defined.find(:example)).to eq(definition)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does not mutate the list' do
|
16
|
+
definition = double('definition')
|
17
|
+
definition_list = Payload::DefinitionList.new
|
18
|
+
|
19
|
+
definition_list.add(:example, definition)
|
20
|
+
|
21
|
+
expect { definition_list.find(:example) }.
|
22
|
+
to raise_error(Payload::UndefinedDependencyError)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#export' do
|
27
|
+
it 'returns a new list with only the given dependencies' do
|
28
|
+
definition_list = Payload::DefinitionList.
|
29
|
+
new.
|
30
|
+
add(:one, 'first').
|
31
|
+
add(:two, 'second').
|
32
|
+
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
|
+
|
44
|
+
exported = definition_list.export([:one, :two])
|
45
|
+
|
46
|
+
expect(exported.find(:one)).to eq(first_exported)
|
47
|
+
expect(exported.find(:two)).to eq(second_exported)
|
48
|
+
expect { exported.find(:three) }.
|
49
|
+
to raise_error(Payload::UndefinedDependencyError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#import' do
|
54
|
+
it 'returns a new list with old dependencies and the given dependencies' do
|
55
|
+
left = Payload::DefinitionList.new.add(:one, 'first')
|
56
|
+
right = Payload::DefinitionList.new.add(:two, 'second')
|
57
|
+
merged = left.import(right)
|
58
|
+
|
59
|
+
expect(merged.find(:one)).to eq('first')
|
60
|
+
expect(merged.find(:two)).to eq('second')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#find' do
|
65
|
+
it 'raises for an unknown definition' do
|
66
|
+
definition_list = Payload::DefinitionList.new
|
67
|
+
|
68
|
+
expect { definition_list.find(:example) }.
|
69
|
+
to raise_error(Payload::UndefinedDependencyError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns an existing definition' do
|
73
|
+
definition = double('definition')
|
74
|
+
definition_list =
|
75
|
+
Payload::DefinitionList.new.add(:example, definition)
|
76
|
+
|
77
|
+
expect(definition_list.find(:example)).to eq(definition)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/exported_definition'
|
3
|
+
require 'payload/testing'
|
4
|
+
|
5
|
+
describe Payload::ExportedDefinition do
|
6
|
+
include Payload::Testing
|
7
|
+
|
8
|
+
describe '#resolve' do
|
9
|
+
it 'finds definitions from both the source and argument containers' do
|
10
|
+
block = lambda do |container|
|
11
|
+
"Ran with #{container[:private]} " \
|
12
|
+
"and #{container[:container]}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private_definition =
|
16
|
+
Payload::ServiceDefinition.new(lambda { |config| 'private' })
|
17
|
+
private_definitions = Payload::DefinitionList.
|
18
|
+
new.
|
19
|
+
add(:private, private_definition)
|
20
|
+
container = build_container.service(:container) { |config| 'container' }
|
21
|
+
definition = Payload::ServiceDefinition.new(block)
|
22
|
+
exported_definition =
|
23
|
+
Payload::ExportedDefinition.new(definition, private_definitions)
|
24
|
+
|
25
|
+
result = exported_definition.resolve(container)
|
26
|
+
|
27
|
+
expect(result).to eq('Ran with private and container')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/factory_definition'
|
3
|
+
require 'payload/testing'
|
4
|
+
|
5
|
+
describe Payload::FactoryDefinition do
|
6
|
+
include Payload::Testing
|
7
|
+
|
8
|
+
describe '#resolve' do
|
9
|
+
it 'returns an object that responds to new' do
|
10
|
+
block = lambda do |config|
|
11
|
+
"#{config[:from_new]} and #{config[:from_container]}"
|
12
|
+
end
|
13
|
+
container = build_container
|
14
|
+
.service(:from_container) { 'From container' }
|
15
|
+
definition = Payload::FactoryDefinition.new(block)
|
16
|
+
|
17
|
+
result = definition.resolve(container).new(from_new: 'From new')
|
18
|
+
|
19
|
+
expect(result).to eq('From new and From container')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#decorate' do
|
24
|
+
it 'returns a decorated object' do
|
25
|
+
block = lambda do |config|
|
26
|
+
"#{config[:from_new]} and #{config[:from_container]}"
|
27
|
+
end
|
28
|
+
first = lambda do |component, config|
|
29
|
+
"Decorated #{component.inspect} with #{config[:from_new]}"
|
30
|
+
end
|
31
|
+
second = lambda do |component, config|
|
32
|
+
"#{component} and #{config[:other]}"
|
33
|
+
end
|
34
|
+
container = build_container
|
35
|
+
.service(:from_container) { 'From container' }
|
36
|
+
.service(:other) { 'Other' }
|
37
|
+
definition = Payload::FactoryDefinition
|
38
|
+
.new(block)
|
39
|
+
.decorate(first)
|
40
|
+
.decorate(second)
|
41
|
+
|
42
|
+
result = definition.resolve(container).new(from_new: 'From new')
|
43
|
+
|
44
|
+
expect(result).to eq(
|
45
|
+
'Decorated "From new and From container" with From new and Other'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/factory'
|
3
|
+
require 'payload/decorator_chain'
|
4
|
+
require 'payload/testing'
|
5
|
+
|
6
|
+
describe Payload::Factory do
|
7
|
+
include Payload::Testing
|
8
|
+
|
9
|
+
describe '#new' do
|
10
|
+
it 'instantiates the dependency' do
|
11
|
+
decorator = lambda do |component, config|
|
12
|
+
"Decorated #{component.inspect} " \
|
13
|
+
"with #{config[:from_container]} " \
|
14
|
+
"and #{config[:from_new]}"
|
15
|
+
end
|
16
|
+
block = lambda do |config|
|
17
|
+
"Component with #{config[:from_container]} and #{config[:from_new]}"
|
18
|
+
end
|
19
|
+
decorators = Payload::DecoratorChain.new.add(decorator)
|
20
|
+
container = build_container
|
21
|
+
.service(:from_container) { 'From container' }
|
22
|
+
factory = Payload::Factory.new(container, block, decorators)
|
23
|
+
|
24
|
+
result = factory.new(from_new: 'From new')
|
25
|
+
|
26
|
+
expect(result).to eq(
|
27
|
+
'Decorated "Component with From container and From new" ' \
|
28
|
+
'with From container and From new'
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'instantiates the dependency without arguments' do
|
33
|
+
block = lambda do |config|
|
34
|
+
"Component with #{config[:from_container]}"
|
35
|
+
end
|
36
|
+
decorators = Payload::DecoratorChain.new
|
37
|
+
container = build_container
|
38
|
+
.service(:from_container) { 'From container' }
|
39
|
+
factory = Payload::Factory.new(container, block, decorators)
|
40
|
+
|
41
|
+
result = factory.new
|
42
|
+
|
43
|
+
expect(result).to eq('Component with From container')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'payload/mutable_container'
|
3
|
+
|
4
|
+
describe Payload::MutableContainer do
|
5
|
+
%w(service factory decorate).each do |method_name|
|
6
|
+
describe "##{method_name}" do
|
7
|
+
it "redefines the container with a #{method_name}" do
|
8
|
+
base_container = double('base_container')
|
9
|
+
modified_container = double('modified_container')
|
10
|
+
allow(base_container).
|
11
|
+
to receive(method_name).
|
12
|
+
with(:example).
|
13
|
+
and_return(modified_container)
|
14
|
+
container = Payload::MutableContainer.new(base_container)
|
15
|
+
|
16
|
+
result = container.send(method_name, :example)
|
17
|
+
|
18
|
+
expect(container.build).to eq(modified_container)
|
19
|
+
expect(result).to eq(container)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#respond_to_missing?' do
|
25
|
+
context 'for a method defined on its base container' do
|
26
|
+
it 'returns true' do
|
27
|
+
base_container = double('base_container')
|
28
|
+
allow(base_container).to receive(:example)
|
29
|
+
container = Payload::MutableContainer.new(base_container)
|
30
|
+
|
31
|
+
expect(container.method(:example)).not_to be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'for an undefined method' do
|
36
|
+
it 'returns false' do
|
37
|
+
base_container = double('base_container')
|
38
|
+
container = Payload::MutableContainer.new(base_container)
|
39
|
+
|
40
|
+
expect { container.method(:unknown) }.to raise_error(NameError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#export' do
|
46
|
+
it 'adds dependencies to the export list' do
|
47
|
+
exports = double('exports')
|
48
|
+
immutable_container = double('base_container')
|
49
|
+
allow(immutable_container).
|
50
|
+
to receive(:service).
|
51
|
+
and_return(immutable_container)
|
52
|
+
allow(immutable_container).
|
53
|
+
to receive(:export).
|
54
|
+
with(:one, :two, :three).
|
55
|
+
and_return(exports)
|
56
|
+
container = Payload::MutableContainer.new(immutable_container)
|
57
|
+
|
58
|
+
container.service(:one)
|
59
|
+
container.service(:two)
|
60
|
+
container.service(:three)
|
61
|
+
container.service(:unexported)
|
62
|
+
container.export(:one, :two)
|
63
|
+
container.export(:three)
|
64
|
+
|
65
|
+
expect(container.exports).to eq(exports)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|