functional-light-service 0.2.4

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 (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