payload 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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