switchyard 7.0.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/.github/workflows/project-build.yml +71 -0
- data/.gitignore +24 -0
- data/.rspec +3 -0
- data/.rubocop.yml +101 -0
- data/.solargraph.yml +11 -0
- data/Appraisals +7 -0
- data/CHANGELOG.md +303 -0
- data/CODE_OF_CONDUCT.md +22 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +1663 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/audit/bench.rb +99 -0
- data/audit/verify_findings.rb +172 -0
- data/gemfiles/dry_inflector_0_2_1.gemfile +5 -0
- data/gemfiles/i18n_1_8_11.gemfile +5 -0
- data/lib/switchyard/action.rb +145 -0
- data/lib/switchyard/configuration.rb +36 -0
- data/lib/switchyard/context/key_verifier.rb +124 -0
- data/lib/switchyard/context.rb +208 -0
- data/lib/switchyard/deprecations.rb +26 -0
- data/lib/switchyard/errors.rb +9 -0
- data/lib/switchyard/functional/enum.rb +286 -0
- data/lib/switchyard/functional/maybe.rb +21 -0
- data/lib/switchyard/functional/monad.rb +77 -0
- data/lib/switchyard/functional/null.rb +88 -0
- data/lib/switchyard/functional/option.rb +100 -0
- data/lib/switchyard/functional/result.rb +129 -0
- data/lib/switchyard/functional/sequencer.rb +144 -0
- data/lib/switchyard/i18n/localization_adapter.rb +50 -0
- data/lib/switchyard/localization_adapter.rb +39 -0
- data/lib/switchyard/localization_map.rb +9 -0
- data/lib/switchyard/organizer/execute.rb +16 -0
- data/lib/switchyard/organizer/iterate.rb +30 -0
- data/lib/switchyard/organizer/reduce_case.rb +50 -0
- data/lib/switchyard/organizer/reduce_if.rb +19 -0
- data/lib/switchyard/organizer/reduce_if_else.rb +23 -0
- data/lib/switchyard/organizer/reduce_until.rb +22 -0
- data/lib/switchyard/organizer/reduce_while.rb +31 -0
- data/lib/switchyard/organizer/scoped_reducable.rb +15 -0
- data/lib/switchyard/organizer/with_callback.rb +28 -0
- data/lib/switchyard/organizer/with_reducer.rb +81 -0
- data/lib/switchyard/organizer/with_reducer_factory.rb +20 -0
- data/lib/switchyard/organizer/with_reducer_log_decorator.rb +111 -0
- data/lib/switchyard/organizer.rb +129 -0
- data/lib/switchyard/testing/context_factory.rb +48 -0
- data/lib/switchyard/testing.rb +3 -0
- data/lib/switchyard/version.rb +5 -0
- data/lib/switchyard.rb +36 -0
- data/resources/fail_actions.png +0 -0
- data/resources/light-service.png +0 -0
- data/resources/organizer_and_actions.png +0 -0
- data/resources/skip_actions.png +0 -0
- data/spec/acceptance/add_numbers_spec.rb +11 -0
- data/spec/acceptance/after_actions_spec.rb +87 -0
- data/spec/acceptance/around_each_spec.rb +19 -0
- data/spec/acceptance/before_actions_spec.rb +115 -0
- data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -0
- data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
- data/spec/acceptance/fail_spec.rb +52 -0
- data/spec/acceptance/log_from_organizer_spec.rb +154 -0
- data/spec/acceptance/message_localization_spec.rb +119 -0
- data/spec/acceptance/organizer/add_aliases_spec.rb +28 -0
- data/spec/acceptance/organizer/add_to_context_spec.rb +54 -0
- data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +42 -0
- data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +90 -0
- data/spec/acceptance/organizer/execute_spec.rb +67 -0
- data/spec/acceptance/organizer/iterate_spec.rb +44 -0
- data/spec/acceptance/organizer/reduce_case_spec.rb +65 -0
- data/spec/acceptance/organizer/reduce_if_else_spec.rb +60 -0
- data/spec/acceptance/organizer/reduce_if_spec.rb +89 -0
- data/spec/acceptance/organizer/reduce_until_spec.rb +49 -0
- data/spec/acceptance/organizer/reduce_while_spec.rb +96 -0
- data/spec/acceptance/organizer/with_callback_spec.rb +113 -0
- data/spec/acceptance/organizer_entry_point_spec.rb +35 -0
- data/spec/acceptance/rollback_spec.rb +183 -0
- data/spec/acceptance/skip_all_remaining_spec.rb +139 -0
- data/spec/acceptance/testing/context_factory_spec.rb +54 -0
- data/spec/action_expected_keys_spec.rb +63 -0
- data/spec/action_expects_and_promises_spec.rb +97 -0
- data/spec/action_optional_expected_keys_spec.rb +107 -0
- data/spec/action_promised_keys_spec.rb +126 -0
- data/spec/action_spec.rb +97 -0
- data/spec/context/inspect_spec.rb +52 -0
- data/spec/context_spec.rb +301 -0
- data/spec/examples/amount_spec.rb +77 -0
- data/spec/examples/controller_spec.rb +63 -0
- data/spec/examples/validate_address_spec.rb +38 -0
- data/spec/i18n_localization_adapter_spec.rb +83 -0
- data/spec/lib/deterministic/class_mixin_spec.rb +24 -0
- data/spec/lib/deterministic/currify_spec.rb +90 -0
- data/spec/lib/deterministic/monad_axioms.rb +46 -0
- data/spec/lib/deterministic/monad_spec.rb +47 -0
- data/spec/lib/deterministic/null_spec.rb +65 -0
- data/spec/lib/deterministic/option_spec.rb +140 -0
- data/spec/lib/deterministic/result/failure_spec.rb +65 -0
- data/spec/lib/deterministic/result/result_map_spec.rb +155 -0
- data/spec/lib/deterministic/result/result_shared.rb +25 -0
- data/spec/lib/deterministic/result/success_spec.rb +41 -0
- data/spec/lib/deterministic/result_spec.rb +63 -0
- data/spec/lib/deterministic/sequencer_spec.rb +506 -0
- data/spec/lib/edge_cases_spec.rb +156 -0
- data/spec/lib/enum_spec.rb +114 -0
- data/spec/lib/native_pattern_matching_spec.rb +74 -0
- data/spec/localization_adapter_spec.rb +66 -0
- data/spec/organizer/with_reducer_spec.rb +56 -0
- data/spec/organizer_key_aliases_spec.rb +29 -0
- data/spec/organizer_spec.rb +115 -0
- data/spec/readme_spec.rb +45 -0
- data/spec/sample/calculates_order_tax_action_spec.rb +16 -0
- data/spec/sample/calculates_tax_spec.rb +30 -0
- data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -0
- data/spec/sample/provides_free_shipping_action_spec.rb +25 -0
- data/spec/sample/tax/calculates_order_tax_action.rb +10 -0
- data/spec/sample/tax/calculates_tax.rb +11 -0
- data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -0
- data/spec/sample/tax/provides_free_shipping_action.rb +11 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support.rb +1 -0
- data/spec/test_doubles.rb +656 -0
- data/spec/testing/context_factory/iterate_spec.rb +39 -0
- data/spec/testing/context_factory/reduce_if_spec.rb +40 -0
- data/spec/testing/context_factory/reduce_until_spec.rb +40 -0
- data/spec/testing/context_factory/with_callback_spec.rb +38 -0
- data/spec/testing/context_factory_spec.rb +76 -0
- data/switchyard.gemspec +35 -0
- metadata +351 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestReduceIfWithAroundEach
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context)
|
|
10
|
+
.around_each(TestDoubles::AroundEachLoggerHandler)
|
|
11
|
+
.reduce(actions)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.actions
|
|
15
|
+
[
|
|
16
|
+
TestDoubles::AddsOneAction,
|
|
17
|
+
reduce_if(->(ctx) { ctx.number == 1 },
|
|
18
|
+
TestDoubles::AddsOneAction)
|
|
19
|
+
]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'can be used to log data' do
|
|
24
|
+
result =
|
|
25
|
+
TestReduceIfWithAroundEach
|
|
26
|
+
.call(:number => 0,
|
|
27
|
+
:logger => TestDoubles::TestLogger.new)
|
|
28
|
+
|
|
29
|
+
expect(result.fetch(:number)).to eq(2)
|
|
30
|
+
expect(result[:logger].logs).to eq(
|
|
31
|
+
[{
|
|
32
|
+
:action => TestDoubles::AddsOneAction,
|
|
33
|
+
:before => 0,
|
|
34
|
+
:after => 1
|
|
35
|
+
}, {
|
|
36
|
+
:action => TestDoubles::AddsOneAction,
|
|
37
|
+
:before => 1,
|
|
38
|
+
:after => 2
|
|
39
|
+
}]
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestSkipBefore
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call
|
|
9
|
+
with(:number => 1)
|
|
10
|
+
.reduce([
|
|
11
|
+
TestDoubles::SkipAllAction,
|
|
12
|
+
reduce_until(->(ctx) { ctx.number == 3 },
|
|
13
|
+
TestDoubles::AddsOneAction)
|
|
14
|
+
])
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class TestSkipAfter
|
|
19
|
+
extend Switchyard::Organizer
|
|
20
|
+
|
|
21
|
+
def self.call
|
|
22
|
+
with(:number => 1)
|
|
23
|
+
.reduce([
|
|
24
|
+
TestDoubles::AddsOneAction,
|
|
25
|
+
reduce_until(->(ctx) { ctx.number == 3 }, [
|
|
26
|
+
TestDoubles::AddsOneAction
|
|
27
|
+
]),
|
|
28
|
+
TestDoubles::SkipAllAction,
|
|
29
|
+
TestDoubles::AddsOneAction
|
|
30
|
+
])
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class TestSkipAllFromNestedScope
|
|
35
|
+
extend Switchyard::Organizer
|
|
36
|
+
|
|
37
|
+
def self.call
|
|
38
|
+
with(:number => 1)
|
|
39
|
+
.reduce([
|
|
40
|
+
reduce_if(->(_ctx) { true }, [
|
|
41
|
+
TestDoubles::AddsOneAction,
|
|
42
|
+
execute(lambda(&:skip_all_remaining!))
|
|
43
|
+
]),
|
|
44
|
+
TestDoubles::AddsOneAction
|
|
45
|
+
])
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class TestContextFailure
|
|
50
|
+
extend Switchyard::Organizer
|
|
51
|
+
|
|
52
|
+
def self.call
|
|
53
|
+
with(:number => 1)
|
|
54
|
+
.reduce([
|
|
55
|
+
TestDoubles::FailureAction,
|
|
56
|
+
reduce_until(->(ctx) { ctx[:number] == 3 },
|
|
57
|
+
TestDoubles::AddsOneAction),
|
|
58
|
+
TestDoubles::AddsOneAction
|
|
59
|
+
])
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'skips all the rest of the actions' do
|
|
64
|
+
result = TestSkipBefore.call
|
|
65
|
+
|
|
66
|
+
expect(result).to be_success
|
|
67
|
+
expect(result[:number]).to eq(1)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'skips after an action in nested context' do
|
|
71
|
+
result = TestSkipAfter.call
|
|
72
|
+
|
|
73
|
+
expect(result).to be_success
|
|
74
|
+
expect(result[:number]).to eq(3)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'does not reset skip_all_remaining at the end of a nested scope' do
|
|
78
|
+
result = TestSkipAllFromNestedScope.call
|
|
79
|
+
|
|
80
|
+
expect(result).to be_success
|
|
81
|
+
expect(result[:number]).to eq(2)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'respects failure across all nestings' do
|
|
85
|
+
result = TestContextFailure.call
|
|
86
|
+
|
|
87
|
+
expect(result).to be_failure
|
|
88
|
+
expect(result[:number]).to eq(1)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestExecute
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(steps)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.steps
|
|
13
|
+
[
|
|
14
|
+
TestDoubles::AddsOneAction,
|
|
15
|
+
execute(->(ctx) { ctx.number += 1 }),
|
|
16
|
+
execute(->(ctx) { ctx[:something] = 'hello' }),
|
|
17
|
+
TestDoubles::AddsOne.actions
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:empty_context) { Switchyard::Context.make }
|
|
23
|
+
|
|
24
|
+
it 'calls the lambda in the execute block using the context' do
|
|
25
|
+
result = TestExecute.call(:number => 0)
|
|
26
|
+
|
|
27
|
+
expect(result).to be_success
|
|
28
|
+
expect(result.number).to eq(3)
|
|
29
|
+
expect(result[:something]).to eq('hello')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'will not execute a failed context' do
|
|
33
|
+
empty_context.fail!('Something bad happened')
|
|
34
|
+
|
|
35
|
+
result = TestExecute.call(empty_context)
|
|
36
|
+
|
|
37
|
+
expect(result).to be_failure
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'does not execute over a skipped context' do
|
|
41
|
+
empty_context.skip_remaining!('No more needed')
|
|
42
|
+
|
|
43
|
+
result = TestExecute.call(empty_context)
|
|
44
|
+
expect(result).to be_success
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'accepts a block instead of a lambda' do
|
|
48
|
+
organizer = Class.new do
|
|
49
|
+
extend Switchyard::Organizer
|
|
50
|
+
|
|
51
|
+
def self.call(context)
|
|
52
|
+
with(context).reduce(steps)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.steps
|
|
56
|
+
[
|
|
57
|
+
execute { |ctx| ctx[:number] += 1 }
|
|
58
|
+
]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
result = organizer.call(:number => 1)
|
|
63
|
+
|
|
64
|
+
expect(result).to be_success
|
|
65
|
+
expect(result[:number]).to eq(2)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
let(:empty_context) { Switchyard::Context.make }
|
|
6
|
+
|
|
7
|
+
it 'reduces each item of a collection and singularizes the collection key' do
|
|
8
|
+
result = TestDoubles::TestIterate.call(:number => 1,
|
|
9
|
+
:counters => [1, 2, 3, 4])
|
|
10
|
+
|
|
11
|
+
expect(result).to be_success
|
|
12
|
+
expect(result.number).to eq(5)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'accepts a single action or organizer' do
|
|
16
|
+
result = TestDoubles::TestIterate.call_single(:number => 1,
|
|
17
|
+
:counters => [1, 2, 3, 4])
|
|
18
|
+
|
|
19
|
+
expect(result).to be_success
|
|
20
|
+
expect(result.number).to eq(5)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "knows that it's being iterated from within an organizer" do
|
|
24
|
+
result = TestDoubles::TestIterate.call(:number => 1,
|
|
25
|
+
:counters => [1, 2, 3, 4])
|
|
26
|
+
|
|
27
|
+
expect(result.organized_by).to eq TestDoubles::TestIterate
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'will not iterate over a failed context' do
|
|
31
|
+
empty_context.fail!('Something bad happened')
|
|
32
|
+
|
|
33
|
+
result = TestDoubles::TestIterate.call(empty_context)
|
|
34
|
+
|
|
35
|
+
expect(result).to be_failure
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'does not iterate over a skipped context' do
|
|
39
|
+
empty_context.skip_remaining!('No more needed')
|
|
40
|
+
|
|
41
|
+
result = TestDoubles::TestIterate.call(empty_context)
|
|
42
|
+
expect(result).to be_success
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestReduceCase
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
reduce_case(
|
|
15
|
+
:value => :incr_num,
|
|
16
|
+
:when => {
|
|
17
|
+
:one => [TestDoubles::AddsOneAction],
|
|
18
|
+
:two => [TestDoubles::AddsTwoAction],
|
|
19
|
+
:three => [TestDoubles::AddsThreeAction]
|
|
20
|
+
},
|
|
21
|
+
:else => [TestDoubles::FailureAction]
|
|
22
|
+
)
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'adds one if the incr_num is one' do
|
|
28
|
+
result = TestReduceCase.call(:number => 0, :incr_num => :one)
|
|
29
|
+
|
|
30
|
+
expect(result).to be_success
|
|
31
|
+
expect(result[:number]).to eq(1)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'adds two if the incr_num is two' do
|
|
35
|
+
result = TestReduceCase.call(:number => 0, :incr_num => :two)
|
|
36
|
+
|
|
37
|
+
expect(result).to be_success
|
|
38
|
+
expect(result[:number]).to eq(2)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'adds three if the incr_num is three' do
|
|
42
|
+
result = TestReduceCase.call(:number => 0, :incr_num => :three)
|
|
43
|
+
|
|
44
|
+
expect(result).to be_success
|
|
45
|
+
expect(result[:number]).to eq(3)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'will fail if the incr_num is neither one, two, or three' do
|
|
49
|
+
result = TestReduceCase.call(:number => 0, :incr_num => :four)
|
|
50
|
+
|
|
51
|
+
expect(result).to be_failure
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'raises ArgumentError when a mandatory keyword argument is missing' do
|
|
55
|
+
expect do
|
|
56
|
+
Class.new do
|
|
57
|
+
extend Switchyard::Organizer
|
|
58
|
+
|
|
59
|
+
def self.actions
|
|
60
|
+
[reduce_case(:value => :incr_num, :when => {})]
|
|
61
|
+
end
|
|
62
|
+
end.actions
|
|
63
|
+
end.to raise_error(ArgumentError, /Expected keyword arguments/)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestReduceIfElse
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
TestDoubles::AddsOneAction,
|
|
15
|
+
reduce_if_else(
|
|
16
|
+
->(ctx) { ctx.number == 1 },
|
|
17
|
+
[TestDoubles::AddsOneAction],
|
|
18
|
+
[TestDoubles::AddsTwoAction]
|
|
19
|
+
)
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:empty_context) { Switchyard::Context.make }
|
|
25
|
+
|
|
26
|
+
it 'reduces the if_steps if the condition is true' do
|
|
27
|
+
result = TestReduceIfElse.call(:number => 0)
|
|
28
|
+
|
|
29
|
+
expect(result).to be_success
|
|
30
|
+
expect(result[:number]).to eq(2)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'reduces the else_steps if the condition is false' do
|
|
34
|
+
result = TestReduceIfElse.call(:number => 2)
|
|
35
|
+
|
|
36
|
+
expect(result).to be_success
|
|
37
|
+
expect(result[:number]).to eq(5)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'will not reduce over a failed context' do
|
|
41
|
+
empty_context.fail!('Something bad happened')
|
|
42
|
+
|
|
43
|
+
result = TestReduceIfElse.call(empty_context)
|
|
44
|
+
|
|
45
|
+
expect(result).to be_failure
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'does not reduce over a skipped context' do
|
|
49
|
+
empty_context.skip_remaining!('No more needed')
|
|
50
|
+
|
|
51
|
+
result = TestReduceIfElse.call(empty_context)
|
|
52
|
+
expect(result).to be_success
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "knows that it's being conditionally reduced from within an organizer" do
|
|
56
|
+
result = TestReduceIfElse.call(:number => 2)
|
|
57
|
+
|
|
58
|
+
expect(result.organized_by).to eq TestReduceIfElse
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestReduceIf
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
TestDoubles::AddsOneAction,
|
|
15
|
+
reduce_if(->(ctx) { ctx.number == 1 },
|
|
16
|
+
TestDoubles::AddsOneAction)
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
let(:empty_context) { Switchyard::Context.make }
|
|
22
|
+
|
|
23
|
+
it 'reduces if the block evaluates to true' do
|
|
24
|
+
result = TestReduceIf.call(:number => 0)
|
|
25
|
+
|
|
26
|
+
expect(result).to be_success
|
|
27
|
+
expect(result[:number]).to eq(2)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'does not reduce if the block evaluates to false' do
|
|
31
|
+
result = TestReduceIf.call(:number => 2)
|
|
32
|
+
|
|
33
|
+
expect(result).to be_success
|
|
34
|
+
expect(result[:number]).to eq(3)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'will not reduce over a failed context' do
|
|
38
|
+
empty_context.fail!('Something bad happened')
|
|
39
|
+
|
|
40
|
+
result = TestReduceIf.call(empty_context)
|
|
41
|
+
|
|
42
|
+
expect(result).to be_failure
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'does not reduce over a skipped context' do
|
|
46
|
+
empty_context.skip_remaining!('No more needed')
|
|
47
|
+
|
|
48
|
+
result = TestReduceIf.call(empty_context)
|
|
49
|
+
expect(result).to be_success
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "knows that it's being conditionally reduced from within an organizer" do
|
|
53
|
+
result = TestReduceIf.call(:number => 2)
|
|
54
|
+
|
|
55
|
+
expect(result.organized_by).to eq TestReduceIf
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'skips actions within in its own scope' do
|
|
59
|
+
org = Class.new do
|
|
60
|
+
extend Switchyard::Organizer
|
|
61
|
+
|
|
62
|
+
def self.call
|
|
63
|
+
reduce(actions)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.actions
|
|
67
|
+
[
|
|
68
|
+
reduce_if(
|
|
69
|
+
->(c) { !c.nil? },
|
|
70
|
+
[
|
|
71
|
+
execute(->(c) { c[:first_reduce_if] = true }),
|
|
72
|
+
execute(->(c) { c.skip_remaining! }), # rubocop:disable Style/SymbolProc
|
|
73
|
+
execute(->(c) { c[:second_reduce_if] = true })
|
|
74
|
+
]
|
|
75
|
+
),
|
|
76
|
+
execute(->(c) { c[:last_outside] = true })
|
|
77
|
+
]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
result = org.call
|
|
82
|
+
|
|
83
|
+
aggregate_failures do
|
|
84
|
+
expect(result[:first_reduce_if]).to be true
|
|
85
|
+
expect(result[:second_reduce_if]).to be_nil
|
|
86
|
+
expect(result[:last_outside]).to be true
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestReduceUntil
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
reduce_until(->(ctx) { ctx.number == 3 },
|
|
15
|
+
TestDoubles::AddsOneAction)
|
|
16
|
+
]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:empty_context) { Switchyard::Context.make }
|
|
21
|
+
|
|
22
|
+
it 'reduces until the block evaluates to true' do
|
|
23
|
+
context = { :number => 1 }
|
|
24
|
+
result = TestReduceUntil.call(context)
|
|
25
|
+
|
|
26
|
+
expect(result).to be_success
|
|
27
|
+
expect(result.number).to eq(3)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'does not execute on failed context' do
|
|
31
|
+
empty_context.fail!('Something bad happened')
|
|
32
|
+
|
|
33
|
+
result = TestReduceUntil.call(empty_context)
|
|
34
|
+
expect(result).to be_failure
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'does not execute a skipped context' do
|
|
38
|
+
empty_context.skip_remaining!('No more needed')
|
|
39
|
+
|
|
40
|
+
result = TestReduceUntil.call(empty_context)
|
|
41
|
+
expect(result).to be_success
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "is expected to know its organizer when reducing until a condition" do
|
|
45
|
+
result = TestReduceUntil.call(:number => 1)
|
|
46
|
+
|
|
47
|
+
expect(result.organized_by).to eq TestReduceUntil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Switchyard::Organizer do
|
|
5
|
+
class TestReduceWhile
|
|
6
|
+
extend Switchyard::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
reduce_while(->(ctx) { ctx[:number] < 3 }, [
|
|
15
|
+
TestDoubles::AddsOneAction,
|
|
16
|
+
TestDoubles::AddsTwoAction
|
|
17
|
+
])
|
|
18
|
+
]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
let(:empty_context) { Switchyard::Context.make }
|
|
23
|
+
|
|
24
|
+
it 'reduces while the block evaluates to true' do
|
|
25
|
+
result = TestReduceWhile.call(:number => 0)
|
|
26
|
+
|
|
27
|
+
expect(result).to be_success
|
|
28
|
+
expect(result[:number]).to eq(3)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'checks the condition before each action' do
|
|
32
|
+
result = TestReduceWhile.call(:number => 2)
|
|
33
|
+
|
|
34
|
+
expect(result).to be_success
|
|
35
|
+
expect(result[:number]).to eq(3)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'does not execute any steps when the condition is false from the start' do
|
|
39
|
+
result = TestReduceWhile.call(:number => 5)
|
|
40
|
+
|
|
41
|
+
expect(result).to be_success
|
|
42
|
+
expect(result[:number]).to eq(5)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'does not execute on failed context' do
|
|
46
|
+
empty_context.fail!('Something bad happened')
|
|
47
|
+
|
|
48
|
+
result = TestReduceWhile.call(empty_context)
|
|
49
|
+
expect(result).to be_failure
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'does not execute a skipped context' do
|
|
53
|
+
empty_context.skip_remaining!('No more needed')
|
|
54
|
+
|
|
55
|
+
result = TestReduceWhile.call(empty_context)
|
|
56
|
+
expect(result).to be_success
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "is expected to know its organizer when reducing while a condition" do
|
|
60
|
+
result = TestReduceWhile.call(:number => 0)
|
|
61
|
+
|
|
62
|
+
expect(result.organized_by).to eq TestReduceWhile
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'skips actions within its own scope' do
|
|
66
|
+
org = Class.new do
|
|
67
|
+
extend Switchyard::Organizer
|
|
68
|
+
|
|
69
|
+
def self.call
|
|
70
|
+
reduce(actions)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.actions
|
|
74
|
+
[
|
|
75
|
+
reduce_while(
|
|
76
|
+
->(c) { !c.nil? },
|
|
77
|
+
[
|
|
78
|
+
execute(->(c) { c[:first_reduce_while] = true }),
|
|
79
|
+
execute(lambda(&:skip_remaining!)),
|
|
80
|
+
execute(->(c) { c[:second_reduce_while] = true })
|
|
81
|
+
]
|
|
82
|
+
),
|
|
83
|
+
execute(->(c) { c[:last_outside] = true })
|
|
84
|
+
]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
result = org.call
|
|
89
|
+
|
|
90
|
+
aggregate_failures do
|
|
91
|
+
expect(result[:first_reduce_while]).to be true
|
|
92
|
+
expect(result[:second_reduce_while]).to be_nil
|
|
93
|
+
expect(result[:last_outside]).to be true
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|