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.
@@ -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