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.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/project-build.yml +71 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +101 -0
  6. data/.solargraph.yml +11 -0
  7. data/Appraisals +7 -0
  8. data/CHANGELOG.md +303 -0
  9. data/CODE_OF_CONDUCT.md +22 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE +22 -0
  12. data/README.md +1663 -0
  13. data/Rakefile +12 -0
  14. data/VERSION +1 -0
  15. data/audit/bench.rb +99 -0
  16. data/audit/verify_findings.rb +172 -0
  17. data/gemfiles/dry_inflector_0_2_1.gemfile +5 -0
  18. data/gemfiles/i18n_1_8_11.gemfile +5 -0
  19. data/lib/switchyard/action.rb +145 -0
  20. data/lib/switchyard/configuration.rb +36 -0
  21. data/lib/switchyard/context/key_verifier.rb +124 -0
  22. data/lib/switchyard/context.rb +208 -0
  23. data/lib/switchyard/deprecations.rb +26 -0
  24. data/lib/switchyard/errors.rb +9 -0
  25. data/lib/switchyard/functional/enum.rb +286 -0
  26. data/lib/switchyard/functional/maybe.rb +21 -0
  27. data/lib/switchyard/functional/monad.rb +77 -0
  28. data/lib/switchyard/functional/null.rb +88 -0
  29. data/lib/switchyard/functional/option.rb +100 -0
  30. data/lib/switchyard/functional/result.rb +129 -0
  31. data/lib/switchyard/functional/sequencer.rb +144 -0
  32. data/lib/switchyard/i18n/localization_adapter.rb +50 -0
  33. data/lib/switchyard/localization_adapter.rb +39 -0
  34. data/lib/switchyard/localization_map.rb +9 -0
  35. data/lib/switchyard/organizer/execute.rb +16 -0
  36. data/lib/switchyard/organizer/iterate.rb +30 -0
  37. data/lib/switchyard/organizer/reduce_case.rb +50 -0
  38. data/lib/switchyard/organizer/reduce_if.rb +19 -0
  39. data/lib/switchyard/organizer/reduce_if_else.rb +23 -0
  40. data/lib/switchyard/organizer/reduce_until.rb +22 -0
  41. data/lib/switchyard/organizer/reduce_while.rb +31 -0
  42. data/lib/switchyard/organizer/scoped_reducable.rb +15 -0
  43. data/lib/switchyard/organizer/with_callback.rb +28 -0
  44. data/lib/switchyard/organizer/with_reducer.rb +81 -0
  45. data/lib/switchyard/organizer/with_reducer_factory.rb +20 -0
  46. data/lib/switchyard/organizer/with_reducer_log_decorator.rb +111 -0
  47. data/lib/switchyard/organizer.rb +129 -0
  48. data/lib/switchyard/testing/context_factory.rb +48 -0
  49. data/lib/switchyard/testing.rb +3 -0
  50. data/lib/switchyard/version.rb +5 -0
  51. data/lib/switchyard.rb +36 -0
  52. data/resources/fail_actions.png +0 -0
  53. data/resources/light-service.png +0 -0
  54. data/resources/organizer_and_actions.png +0 -0
  55. data/resources/skip_actions.png +0 -0
  56. data/spec/acceptance/add_numbers_spec.rb +11 -0
  57. data/spec/acceptance/after_actions_spec.rb +87 -0
  58. data/spec/acceptance/around_each_spec.rb +19 -0
  59. data/spec/acceptance/before_actions_spec.rb +115 -0
  60. data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -0
  61. data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
  62. data/spec/acceptance/fail_spec.rb +52 -0
  63. data/spec/acceptance/log_from_organizer_spec.rb +154 -0
  64. data/spec/acceptance/message_localization_spec.rb +119 -0
  65. data/spec/acceptance/organizer/add_aliases_spec.rb +28 -0
  66. data/spec/acceptance/organizer/add_to_context_spec.rb +54 -0
  67. data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +42 -0
  68. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +90 -0
  69. data/spec/acceptance/organizer/execute_spec.rb +67 -0
  70. data/spec/acceptance/organizer/iterate_spec.rb +44 -0
  71. data/spec/acceptance/organizer/reduce_case_spec.rb +65 -0
  72. data/spec/acceptance/organizer/reduce_if_else_spec.rb +60 -0
  73. data/spec/acceptance/organizer/reduce_if_spec.rb +89 -0
  74. data/spec/acceptance/organizer/reduce_until_spec.rb +49 -0
  75. data/spec/acceptance/organizer/reduce_while_spec.rb +96 -0
  76. data/spec/acceptance/organizer/with_callback_spec.rb +113 -0
  77. data/spec/acceptance/organizer_entry_point_spec.rb +35 -0
  78. data/spec/acceptance/rollback_spec.rb +183 -0
  79. data/spec/acceptance/skip_all_remaining_spec.rb +139 -0
  80. data/spec/acceptance/testing/context_factory_spec.rb +54 -0
  81. data/spec/action_expected_keys_spec.rb +63 -0
  82. data/spec/action_expects_and_promises_spec.rb +97 -0
  83. data/spec/action_optional_expected_keys_spec.rb +107 -0
  84. data/spec/action_promised_keys_spec.rb +126 -0
  85. data/spec/action_spec.rb +97 -0
  86. data/spec/context/inspect_spec.rb +52 -0
  87. data/spec/context_spec.rb +301 -0
  88. data/spec/examples/amount_spec.rb +77 -0
  89. data/spec/examples/controller_spec.rb +63 -0
  90. data/spec/examples/validate_address_spec.rb +38 -0
  91. data/spec/i18n_localization_adapter_spec.rb +83 -0
  92. data/spec/lib/deterministic/class_mixin_spec.rb +24 -0
  93. data/spec/lib/deterministic/currify_spec.rb +90 -0
  94. data/spec/lib/deterministic/monad_axioms.rb +46 -0
  95. data/spec/lib/deterministic/monad_spec.rb +47 -0
  96. data/spec/lib/deterministic/null_spec.rb +65 -0
  97. data/spec/lib/deterministic/option_spec.rb +140 -0
  98. data/spec/lib/deterministic/result/failure_spec.rb +65 -0
  99. data/spec/lib/deterministic/result/result_map_spec.rb +155 -0
  100. data/spec/lib/deterministic/result/result_shared.rb +25 -0
  101. data/spec/lib/deterministic/result/success_spec.rb +41 -0
  102. data/spec/lib/deterministic/result_spec.rb +63 -0
  103. data/spec/lib/deterministic/sequencer_spec.rb +506 -0
  104. data/spec/lib/edge_cases_spec.rb +156 -0
  105. data/spec/lib/enum_spec.rb +114 -0
  106. data/spec/lib/native_pattern_matching_spec.rb +74 -0
  107. data/spec/localization_adapter_spec.rb +66 -0
  108. data/spec/organizer/with_reducer_spec.rb +56 -0
  109. data/spec/organizer_key_aliases_spec.rb +29 -0
  110. data/spec/organizer_spec.rb +115 -0
  111. data/spec/readme_spec.rb +45 -0
  112. data/spec/sample/calculates_order_tax_action_spec.rb +16 -0
  113. data/spec/sample/calculates_tax_spec.rb +30 -0
  114. data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -0
  115. data/spec/sample/provides_free_shipping_action_spec.rb +25 -0
  116. data/spec/sample/tax/calculates_order_tax_action.rb +10 -0
  117. data/spec/sample/tax/calculates_tax.rb +11 -0
  118. data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -0
  119. data/spec/sample/tax/provides_free_shipping_action.rb +11 -0
  120. data/spec/spec_helper.rb +32 -0
  121. data/spec/support.rb +1 -0
  122. data/spec/test_doubles.rb +656 -0
  123. data/spec/testing/context_factory/iterate_spec.rb +39 -0
  124. data/spec/testing/context_factory/reduce_if_spec.rb +40 -0
  125. data/spec/testing/context_factory/reduce_until_spec.rb +40 -0
  126. data/spec/testing/context_factory/with_callback_spec.rb +38 -0
  127. data/spec/testing/context_factory_spec.rb +76 -0
  128. data/switchyard.gemspec +35 -0
  129. 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