functional-light-service 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +72 -0
  5. data/.travis.yml +22 -0
  6. data/Appraisals +3 -0
  7. data/CHANGELOG.md +37 -0
  8. data/CODE_OF_CONDUCT.md +22 -0
  9. data/Gemfile +6 -0
  10. data/LICENSE +22 -0
  11. data/README.md +1424 -0
  12. data/Rakefile +12 -0
  13. data/VERSION +1 -0
  14. data/functional-light-service.gemspec +26 -0
  15. data/gemfiles/activesupport_5.gemfile +8 -0
  16. data/gemfiles/activesupport_5.gemfile.lock +82 -0
  17. data/lib/functional-light-service/action.rb +102 -0
  18. data/lib/functional-light-service/configuration.rb +24 -0
  19. data/lib/functional-light-service/context/key_verifier.rb +118 -0
  20. data/lib/functional-light-service/context.rb +165 -0
  21. data/lib/functional-light-service/errors.rb +6 -0
  22. data/lib/functional-light-service/functional/enum.rb +254 -0
  23. data/lib/functional-light-service/functional/maybe.rb +14 -0
  24. data/lib/functional-light-service/functional/monad.rb +66 -0
  25. data/lib/functional-light-service/functional/null.rb +74 -0
  26. data/lib/functional-light-service/functional/option.rb +99 -0
  27. data/lib/functional-light-service/functional/result.rb +122 -0
  28. data/lib/functional-light-service/localization_adapter.rb +44 -0
  29. data/lib/functional-light-service/organizer/execute.rb +14 -0
  30. data/lib/functional-light-service/organizer/iterate.rb +22 -0
  31. data/lib/functional-light-service/organizer/reduce_if.rb +17 -0
  32. data/lib/functional-light-service/organizer/reduce_until.rb +20 -0
  33. data/lib/functional-light-service/organizer/scoped_reducable.rb +13 -0
  34. data/lib/functional-light-service/organizer/verify_call_method_exists.rb +28 -0
  35. data/lib/functional-light-service/organizer/with_callback.rb +26 -0
  36. data/lib/functional-light-service/organizer/with_reducer.rb +71 -0
  37. data/lib/functional-light-service/organizer/with_reducer_factory.rb +18 -0
  38. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +105 -0
  39. data/lib/functional-light-service/organizer.rb +105 -0
  40. data/lib/functional-light-service/testing/context_factory.rb +40 -0
  41. data/lib/functional-light-service/testing.rb +1 -0
  42. data/lib/functional-light-service/version.rb +3 -0
  43. data/lib/functional-light-service.rb +29 -0
  44. data/resources/fail_actions.png +0 -0
  45. data/resources/light-service.png +0 -0
  46. data/resources/organizer_and_actions.png +0 -0
  47. data/resources/skip_actions.png +0 -0
  48. data/spec/acceptance/add_numbers_spec.rb +11 -0
  49. data/spec/acceptance/after_actions_spec.rb +71 -0
  50. data/spec/acceptance/around_each_spec.rb +19 -0
  51. data/spec/acceptance/before_actions_spec.rb +98 -0
  52. data/spec/acceptance/custom_log_from_organizer_spec.rb +60 -0
  53. data/spec/acceptance/fail_spec.rb +24 -0
  54. data/spec/acceptance/include_warning_spec.rb +29 -0
  55. data/spec/acceptance/log_from_organizer_spec.rb +154 -0
  56. data/spec/acceptance/message_localization_spec.rb +118 -0
  57. data/spec/acceptance/not_having_call_method_warning_spec.rb +39 -0
  58. data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +42 -0
  59. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +65 -0
  60. data/spec/acceptance/organizer/execute_spec.rb +46 -0
  61. data/spec/acceptance/organizer/iterate_spec.rb +37 -0
  62. data/spec/acceptance/organizer/reduce_if_spec.rb +51 -0
  63. data/spec/acceptance/organizer/reduce_until_spec.rb +43 -0
  64. data/spec/acceptance/organizer/with_callback_spec.rb +110 -0
  65. data/spec/acceptance/rollback_spec.rb +132 -0
  66. data/spec/acceptance/skip_all_warning_spec.rb +20 -0
  67. data/spec/acceptance/testing/context_factory_spec.rb +54 -0
  68. data/spec/action_expected_keys_spec.rb +63 -0
  69. data/spec/action_expects_and_promises_spec.rb +93 -0
  70. data/spec/action_promised_keys_spec.rb +122 -0
  71. data/spec/action_spec.rb +89 -0
  72. data/spec/context/inspect_spec.rb +57 -0
  73. data/spec/context_spec.rb +197 -0
  74. data/spec/examples/amount_spec.rb +77 -0
  75. data/spec/examples/controller_spec.rb +63 -0
  76. data/spec/examples/validate_address_spec.rb +37 -0
  77. data/spec/lib/deterministic/class_mixin_spec.rb +24 -0
  78. data/spec/lib/deterministic/currify_spec.rb +88 -0
  79. data/spec/lib/deterministic/monad_axioms.rb +44 -0
  80. data/spec/lib/deterministic/monad_spec.rb +45 -0
  81. data/spec/lib/deterministic/null_spec.rb +58 -0
  82. data/spec/lib/deterministic/option_spec.rb +133 -0
  83. data/spec/lib/deterministic/result/failure_spec.rb +65 -0
  84. data/spec/lib/deterministic/result/result_map_spec.rb +154 -0
  85. data/spec/lib/deterministic/result/result_shared.rb +24 -0
  86. data/spec/lib/deterministic/result/success_spec.rb +41 -0
  87. data/spec/lib/deterministic/result_spec.rb +63 -0
  88. data/spec/lib/enum_spec.rb +112 -0
  89. data/spec/localization_adapter_spec.rb +83 -0
  90. data/spec/organizer/with_reducer_spec.rb +56 -0
  91. data/spec/organizer_key_aliases_spec.rb +29 -0
  92. data/spec/organizer_spec.rb +93 -0
  93. data/spec/readme_spec.rb +47 -0
  94. data/spec/sample/calculates_order_tax_action_spec.rb +16 -0
  95. data/spec/sample/calculates_tax_spec.rb +30 -0
  96. data/spec/sample/looks_up_tax_percentage_action_spec.rb +53 -0
  97. data/spec/sample/provides_free_shipping_action_spec.rb +25 -0
  98. data/spec/sample/tax/calculates_order_tax_action.rb +9 -0
  99. data/spec/sample/tax/calculates_tax.rb +11 -0
  100. data/spec/sample/tax/looks_up_tax_percentage_action.rb +27 -0
  101. data/spec/sample/tax/provides_free_shipping_action.rb +10 -0
  102. data/spec/spec_helper.rb +24 -0
  103. data/spec/support.rb +1 -0
  104. data/spec/test_doubles.rb +552 -0
  105. data/spec/testing/context_factory/iterate_spec.rb +39 -0
  106. data/spec/testing/context_factory/reduce_if_spec.rb +40 -0
  107. data/spec/testing/context_factory/reduce_until_spec.rb +40 -0
  108. data/spec/testing/context_factory/with_callback_spec.rb +38 -0
  109. data/spec/testing/context_factory_spec.rb +55 -0
  110. metadata +285 -0
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe FunctionalLightService::Organizer do
5
+ let(:empty_context) { FunctionalLightService::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 'will not iterate over a failed context' do
24
+ empty_context.fail!('Something bad happened')
25
+
26
+ result = TestDoubles::TestIterate.call(empty_context)
27
+
28
+ expect(result).to be_failure
29
+ end
30
+
31
+ it 'does not iterate over a skipped context' do
32
+ empty_context.skip_remaining!('No more needed')
33
+
34
+ result = TestDoubles::TestIterate.call(empty_context)
35
+ expect(result).to be_success
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe FunctionalLightService::Organizer do
5
+ class TestReduceIf
6
+ extend FunctionalLightService::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) { FunctionalLightService::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
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe FunctionalLightService::Organizer do
5
+ class TestReduceUntil
6
+ extend FunctionalLightService::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) { FunctionalLightService::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
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe FunctionalLightService::Organizer do
5
+ describe 'a simple case with a single callback' do
6
+ it 'calls the actions defined with callback' do
7
+ result = TestDoubles::TestWithCallback.call
8
+
9
+ expect(result.counter).to eq(3)
10
+ expect(result.total).to eq(6)
11
+ end
12
+ end
13
+
14
+ describe 'a more complex example with nested callbacks' do
15
+ class TestWithNestedCallback
16
+ extend FunctionalLightService::Organizer
17
+
18
+ def self.call(context = {})
19
+ with(context).reduce(actions)
20
+ end
21
+
22
+ def self.actions
23
+ [
24
+ SetUpNestedContextAction,
25
+ with_callback(IterateOuterCollectionAction,
26
+ [IncrementOuterCountAction,
27
+ with_callback(TestDoubles::IterateCollectionAction,
28
+ [TestDoubles::IncrementCountAction,
29
+ TestDoubles::AddToTotalAction])])
30
+ ]
31
+ end
32
+ end
33
+
34
+ class SetUpNestedContextAction
35
+ extend FunctionalLightService::Action
36
+ promises :outer_numbers, :outer_counter,
37
+ :numbers, :counter, :total
38
+
39
+ executed do |ctx|
40
+ ctx.outer_numbers = [12, 17]
41
+ ctx.outer_counter = 0
42
+ ctx.numbers = [1, 2, 3]
43
+ ctx.counter = 0
44
+ ctx.total = 0
45
+ end
46
+ end
47
+
48
+ class IterateOuterCollectionAction
49
+ extend FunctionalLightService::Action
50
+ expects :outer_numbers, :callback
51
+ promises :outer_number
52
+
53
+ executed do |ctx|
54
+ ctx.outer_numbers.each do |outer_number|
55
+ ctx.outer_number = outer_number
56
+ ctx.callback.call(ctx)
57
+ end
58
+ end
59
+ end
60
+
61
+ class IncrementOuterCountAction
62
+ extend FunctionalLightService::Action
63
+ expects :outer_counter
64
+
65
+ executed do |ctx|
66
+ ctx.outer_counter = ctx.outer_counter + 1
67
+ end
68
+ end
69
+
70
+ it 'calls both the action and the nested callbacks' do
71
+ result = TestWithNestedCallback.call
72
+
73
+ expect(result.outer_counter).to eq(2)
74
+ # Counts and total are the duplicates of
75
+ # what you'll see in the simple spec,
76
+ # as the internal callback logic is called
77
+ # twice due to 2 items in the outer_numbers
78
+ # collection.
79
+ expect(result.counter).to eq(6)
80
+ expect(result.total).to eq(12)
81
+ end
82
+ end
83
+
84
+ describe 'with failed or skipped context' do
85
+ class TestWithFailureCallback
86
+ extend FunctionalLightService::Organizer
87
+
88
+ def self.call(context = {})
89
+ with(context).reduce(actions)
90
+ end
91
+
92
+ def self.actions
93
+ [
94
+ TestDoubles::SetUpContextAction,
95
+ with_callback(TestDoubles::IterateCollectionAction,
96
+ [TestDoubles::IncrementCountAction,
97
+ TestDoubles::FailureAction])
98
+ ]
99
+ end
100
+ end
101
+
102
+ it 'will not process the routine' do
103
+ result = TestWithFailureCallback.call
104
+
105
+ expect(result).to be_failure
106
+ expect(result.counter).to eq(1)
107
+ expect(result.total).to eq(0)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ class RollbackOrganizer
5
+ extend FunctionalLightService::Organizer
6
+
7
+ def self.call(number)
8
+ with(:number => number).reduce(
9
+ AddsOneWithRollbackAction,
10
+ TestDoubles::AddsTwoAction,
11
+ AddsThreeWithRollbackAction
12
+ )
13
+ end
14
+ end
15
+
16
+ class AddsOneWithRollbackAction
17
+ extend FunctionalLightService::Action
18
+ expects :number
19
+ promises :number
20
+
21
+ executed do |context|
22
+ context.fail_with_rollback! if context.number.zero?
23
+
24
+ context.number += 1
25
+ end
26
+
27
+ rolled_back do |context|
28
+ context.number -= 1
29
+ end
30
+ end
31
+
32
+ class AddsThreeWithRollbackAction
33
+ extend FunctionalLightService::Action
34
+ expects :number
35
+
36
+ executed do |context|
37
+ context.number = context.number + 3
38
+
39
+ context.fail_with_rollback!("I did not like this!")
40
+ end
41
+
42
+ rolled_back do |context|
43
+ context.number -= 3
44
+ end
45
+ end
46
+
47
+ class RollbackOrganizerWithNoRollback
48
+ extend FunctionalLightService::Organizer
49
+
50
+ def self.call(number)
51
+ with(:number => number).reduce(
52
+ TestDoubles::AddsOneAction,
53
+ TestDoubles::AddsTwoAction,
54
+ AddsThreeWithNoRollbackAction
55
+ )
56
+ end
57
+ end
58
+
59
+ class AddsThreeWithNoRollbackAction
60
+ extend FunctionalLightService::Action
61
+ expects :number
62
+
63
+ executed do |context|
64
+ context.number = context.number + 3
65
+
66
+ context.fail_with_rollback!("I did not like this!")
67
+ end
68
+ end
69
+
70
+ class RollbackOrganizerWithMiddleRollback
71
+ extend FunctionalLightService::Organizer
72
+
73
+ def self.call(number)
74
+ with(:number => number).reduce(
75
+ TestDoubles::AddsOneAction,
76
+ AddsTwoActionWithRollback,
77
+ TestDoubles::AddsThreeAction
78
+ )
79
+ end
80
+ end
81
+
82
+ class AddsTwoActionWithRollback
83
+ extend FunctionalLightService::Action
84
+ expects :number
85
+
86
+ executed do |context|
87
+ context.number = context.number + 2
88
+
89
+ context.fail_with_rollback!("I did not like this a bit!")
90
+ end
91
+
92
+ rolled_back do |context|
93
+ context.number -= 2
94
+ end
95
+ end
96
+
97
+ describe "Rolling back actions when there is a failure" do
98
+ it "Adds 1, 2, 3 to 1 and rolls back " do
99
+ result = RollbackOrganizer.call 1
100
+ number = result.fetch(:number)
101
+
102
+ expect(result).to be_failure
103
+ expect(result.message).to eq("I did not like this!")
104
+ expect(number).to eq(3)
105
+ end
106
+
107
+ it "won't error out when actions don't define rollback" do
108
+ result = RollbackOrganizerWithNoRollback.call 1
109
+ number = result.fetch(:number)
110
+
111
+ expect(result).to be_failure
112
+ expect(result.message).to eq("I did not like this!")
113
+ expect(number).to eq(7)
114
+ end
115
+
116
+ it "rolls back properly when triggered with an action in the middle" do
117
+ result = RollbackOrganizerWithMiddleRollback.call 1
118
+ number = result.fetch(:number)
119
+
120
+ expect(result).to be_failure
121
+ expect(result.message).to eq("I did not like this a bit!")
122
+ expect(number).to eq(2)
123
+ end
124
+
125
+ it "rolls back from the first action" do
126
+ result = RollbackOrganizer.call 0
127
+ number = result.fetch(:number)
128
+
129
+ expect(result).to be_failure
130
+ expect(number).to eq(-1)
131
+ end
132
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "skip_all! has been deprecated" do
4
+ it "is now skip_remaining!" do
5
+ class SkipAllDeprecatedAction
6
+ extend FunctionalLightService::Action
7
+
8
+ executed do |ctx|
9
+ ctx.skip_all!("No need to execute other actions.")
10
+ end
11
+ end
12
+
13
+ expected_msg = "Using skip_all! has been deprecated, " \
14
+ "please use `skip_remaining!` instead."
15
+ expect(ActiveSupport::Deprecation).to receive(:warn)
16
+ .with(expected_msg)
17
+
18
+ SkipAllDeprecatedAction.execute
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ class AdditionOrganizerContextFactory
5
+ def self.make_for(action, number)
6
+ number += 3 # You can add more logic to prepare your context
7
+
8
+ FunctionalLightService::Testing::ContextFactory
9
+ .make_from(TestDoubles::AdditionOrganizer)
10
+ .for(action)
11
+ .with(number)
12
+ end
13
+ end
14
+
15
+ RSpec.describe TestDoubles::AddsThreeAction do
16
+ it 'creates a context for the action with ContextFactory wrapper' do
17
+ context =
18
+ AdditionOrganizerContextFactory
19
+ .make_for(TestDoubles::AddsThreeAction, 1)
20
+
21
+ expect(context.number).to eq(7)
22
+ end
23
+
24
+ it 'creates a context for the action using the ContextFactory' do
25
+ context =
26
+ FunctionalLightService::Testing::ContextFactory
27
+ .make_from(TestDoubles::AdditionOrganizer)
28
+ .for(TestDoubles::AddsThreeAction)
29
+ .with(4) # Context is a "glorified" hash
30
+
31
+ expect(context.number).to eq(7)
32
+ end
33
+
34
+ it "works with multiple arguments passed to Organizer's call method" do
35
+ context = FunctionalLightService::Testing::ContextFactory
36
+ .make_from(TestDoubles::ExtraArgumentAdditionOrganizer)
37
+ .for(described_class)
38
+ .with(4, 2)
39
+
40
+ expect(context.number).to eq(9)
41
+ end
42
+ end
43
+
44
+ RSpec.describe TestDoubles::AddsTwoAction do
45
+ it 'does not execute a callback entirely from a ContextFactory' do
46
+ context = FunctionalLightService::Testing::ContextFactory
47
+ .make_from(TestDoubles::CallbackOrganizer)
48
+ .for(described_class)
49
+ .with(:number => 0)
50
+
51
+ # add 1, add 10, then stop before executing first add 2
52
+ expect(context.number).to eq(11)
53
+ end
54
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe ":expects macro" do
5
+ context "when expected keys are in the context" do
6
+ it "can access the keys as class methods" do
7
+ resulting_context = TestDoubles::MakesTeaWithMilkAction.execute(
8
+ :tea => "black",
9
+ :milk => "full cream",
10
+ :something => "else"
11
+ )
12
+ expect(resulting_context[:milk_tea]).to eq("black - full cream")
13
+ end
14
+ end
15
+
16
+ context "when an expected key is not in the context" do
17
+ it "raises an FunctionalLightService::ExpectedKeysNotInContextError" do
18
+ exception_msg = "expected :milk to be in the context during " \
19
+ "TestDoubles::MakesTeaWithMilkAction"
20
+ expect do
21
+ TestDoubles::MakesTeaWithMilkAction.execute(:tea => "black")
22
+ end.to \
23
+ raise_error(FunctionalLightService::ExpectedKeysNotInContextError, exception_msg)
24
+ end
25
+ end
26
+
27
+ context "when the `expects` macro is called multiple times" do
28
+ it "can collect expected keys " do
29
+ result = TestDoubles::MultipleExpectsAction.execute(
30
+ :tea => "black",
31
+ :milk => "full cream",
32
+ :chocolate => "dark chocolate"
33
+ )
34
+ expect(result[:milk_tea]).to \
35
+ eq("black - full cream - with dark chocolate")
36
+ end
37
+ end
38
+
39
+ context "when a reserved key is listed as an expected key" do
40
+ it "raises an error indicating a reserved key is expected" do
41
+ exception_msg = "promised or expected keys cannot be a reserved key: " \
42
+ "[:message]"
43
+ expect do
44
+ TestDoubles::MakesTeaExpectingReservedKey.execute(:tea => "black",
45
+ :message => "no no")
46
+ end.to \
47
+ raise_error(FunctionalLightService::ReservedKeysInContextError, exception_msg)
48
+ end
49
+
50
+ it "raises an error indicating that multiple reserved keys are expected" do
51
+ exception_msg = "promised or expected keys cannot be a reserved key: " \
52
+ "[:message, :error_code, :current_action]"
53
+ expect do
54
+ TestDoubles::MakesTeaExpectingMultipleReservedKeys
55
+ .execute(:tea => "black",
56
+ :message => "no no",
57
+ :error_code => 1,
58
+ :current_action => "update")
59
+ end.to raise_error(FunctionalLightService::ReservedKeysInContextError,
60
+ exception_msg)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe ":expects and :promises macros" do
4
+ describe "actions are backward compatible" do
5
+ class FooAction
6
+ extend FunctionalLightService::Action
7
+
8
+ executed do |context|
9
+ baz = context.fetch :baz
10
+
11
+ bar = baz + 2
12
+ context[:bar] = bar
13
+ end
14
+ end
15
+ it "works without expects and promises" do
16
+ result = FooAction.execute(:baz => 3)
17
+ expect(result).to be_success
18
+ expect(result[:bar]).to eq(5)
19
+ end
20
+ end
21
+
22
+ context "when expected keys are not in context" do
23
+ class FooNoExpectedKeyAction
24
+ extend FunctionalLightService::Action
25
+ expects :baz
26
+
27
+ executed do |context|
28
+ baz = context.fetch :baz
29
+
30
+ bar = baz + 2
31
+ context[:bar] = bar
32
+ end
33
+ end
34
+ it "throws an ExpectedKeysNotInContextError" do
35
+ # FooAction invoked with nothing in the context
36
+ expect { FooNoExpectedKeyAction.execute }.to \
37
+ raise_error(FunctionalLightService::ExpectedKeysNotInContextError)
38
+ end
39
+ end
40
+
41
+ describe "expected keys" do
42
+ class FooWithReaderAction
43
+ extend FunctionalLightService::Action
44
+ expects :baz
45
+
46
+ executed do |context|
47
+ # Notice how I use `context.baz` here
48
+ bar = context.baz + 2
49
+ context[:bar] = bar
50
+ end
51
+ end
52
+ it "can be accessed through a reader" do
53
+ result = FooWithReaderAction.execute(:baz => 3)
54
+ expect(result).to be_success
55
+ expect(result[:bar]).to eq(5)
56
+ end
57
+ end
58
+
59
+ context "when promised keys are not in context" do
60
+ class FooNoPromisedKeyAction
61
+ extend FunctionalLightService::Action
62
+ expects :baz
63
+ promises :bar
64
+
65
+ executed do |context|
66
+ # I am not adding anything to the context
67
+ end
68
+ end
69
+ it "throws a PromisedKeysNotInContextError" do
70
+ # FooAction invoked with nothing placed in the context
71
+ expect { FooNoPromisedKeyAction.execute(:baz => 3) }.to \
72
+ raise_error(FunctionalLightService::PromisedKeysNotInContextError)
73
+ end
74
+ end
75
+
76
+ describe "promised keys" do
77
+ class FooWithExpectsAndPromisesAction
78
+ extend FunctionalLightService::Action
79
+ expects :baz
80
+ promises :bar
81
+
82
+ executed do |context|
83
+ # Notice how I use `context.bar` here
84
+ context.bar = context.baz + 2
85
+ end
86
+ end
87
+ it "puts the value through the accessor into the context" do
88
+ result = FooWithExpectsAndPromisesAction.execute(:baz => 3)
89
+ expect(result).to be_success
90
+ expect(result[:bar]).to eq(5)
91
+ end
92
+ end
93
+ end