functional-light-service 0.5.4 → 6.1.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/project-build.yml +35 -11
  3. data/.rubocop.yml +101 -160
  4. data/AUDIT-functional-light-service.md +352 -0
  5. data/CHANGELOG.md +45 -0
  6. data/README.md +88 -2
  7. data/audit/bench.rb +99 -0
  8. data/audit/verify_findings.rb +172 -0
  9. data/functional-light-service.gemspec +15 -21
  10. data/lib/functional-light-service/action.rb +97 -101
  11. data/lib/functional-light-service/configuration.rb +26 -24
  12. data/lib/functional-light-service/context/key_verifier.rb +124 -118
  13. data/lib/functional-light-service/context.rb +63 -20
  14. data/lib/functional-light-service/deprecations.rb +26 -0
  15. data/lib/functional-light-service/errors.rb +8 -6
  16. data/lib/functional-light-service/functional/enum.rb +286 -250
  17. data/lib/functional-light-service/functional/maybe.rb +21 -15
  18. data/lib/functional-light-service/functional/monad.rb +77 -66
  19. data/lib/functional-light-service/functional/null.rb +88 -74
  20. data/lib/functional-light-service/functional/option.rb +100 -97
  21. data/lib/functional-light-service/functional/result.rb +129 -116
  22. data/lib/functional-light-service/functional/sequencer.rb +144 -0
  23. data/lib/functional-light-service/localization_adapter.rb +48 -47
  24. data/lib/functional-light-service/organizer/execute.rb +16 -14
  25. data/lib/functional-light-service/organizer/iterate.rb +30 -25
  26. data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
  27. data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
  28. data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
  29. data/lib/functional-light-service/organizer/with_callback.rb +28 -26
  30. data/lib/functional-light-service/organizer/with_reducer.rb +81 -77
  31. data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
  32. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -108
  33. data/lib/functional-light-service/organizer.rb +114 -114
  34. data/lib/functional-light-service/testing/context_factory.rb +48 -42
  35. data/lib/functional-light-service/testing.rb +3 -1
  36. data/lib/functional-light-service/version.rb +5 -3
  37. data/lib/functional-light-service.rb +31 -28
  38. data/spec/acceptance/after_actions_spec.rb +87 -71
  39. data/spec/acceptance/before_actions_spec.rb +115 -98
  40. data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
  41. data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
  42. data/spec/acceptance/fail_spec.rb +52 -50
  43. data/spec/acceptance/message_localization_spec.rb +119 -118
  44. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
  45. data/spec/acceptance/organizer/reduce_if_spec.rb +89 -89
  46. data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
  47. data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
  48. data/spec/acceptance/rollback_spec.rb +183 -132
  49. data/spec/action_expects_and_promises_spec.rb +97 -93
  50. data/spec/action_promised_keys_spec.rb +126 -122
  51. data/spec/context_spec.rb +289 -197
  52. data/spec/examples/controller_spec.rb +63 -63
  53. data/spec/examples/validate_address_spec.rb +38 -37
  54. data/spec/lib/deterministic/currify_spec.rb +90 -88
  55. data/spec/lib/deterministic/null_spec.rb +6 -1
  56. data/spec/lib/deterministic/option_spec.rb +140 -137
  57. data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
  58. data/spec/lib/deterministic/result/result_shared.rb +3 -2
  59. data/spec/lib/deterministic/result_spec.rb +2 -2
  60. data/spec/lib/deterministic/sequencer_spec.rb +506 -0
  61. data/spec/lib/edge_cases_spec.rb +156 -0
  62. data/spec/lib/enum_spec.rb +1 -1
  63. data/spec/lib/native_pattern_matching_spec.rb +74 -0
  64. data/spec/organizer_spec.rb +115 -114
  65. data/spec/readme_spec.rb +45 -47
  66. data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
  67. data/spec/sample/calculates_tax_spec.rb +1 -1
  68. data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
  69. data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
  70. data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
  71. data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
  72. data/spec/spec_helper.rb +6 -0
  73. data/spec/test_doubles.rb +628 -599
  74. data/spec/testing/context_factory_spec.rb +21 -0
  75. metadata +48 -162
  76. data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
  77. data/spec/acceptance/include_warning_spec.rb +0 -29
@@ -1,93 +1,97 @@
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
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
+
26
+ expects :baz
27
+
28
+ executed do |context|
29
+ baz = context.fetch :baz
30
+
31
+ bar = baz + 2
32
+ context[:bar] = bar
33
+ end
34
+ end
35
+ it "throws an ExpectedKeysNotInContextError" do
36
+ # FooAction invoked with nothing in the context
37
+ expect { FooNoExpectedKeyAction.execute }.to \
38
+ raise_error(FunctionalLightService::ExpectedKeysNotInContextError)
39
+ end
40
+ end
41
+
42
+ describe "expected keys" do
43
+ class FooWithReaderAction
44
+ extend FunctionalLightService::Action
45
+
46
+ expects :baz
47
+
48
+ executed do |context|
49
+ # Notice how I use `context.baz` here
50
+ bar = context.baz + 2
51
+ context[:bar] = bar
52
+ end
53
+ end
54
+ it "can be accessed through a reader" do
55
+ result = FooWithReaderAction.execute(:baz => 3)
56
+ expect(result).to be_success
57
+ expect(result[:bar]).to eq(5)
58
+ end
59
+ end
60
+
61
+ context "when promised keys are not in context" do
62
+ class FooNoPromisedKeyAction
63
+ extend FunctionalLightService::Action
64
+
65
+ expects :baz
66
+ promises :bar
67
+
68
+ executed do |context|
69
+ # I am not adding anything to the context
70
+ end
71
+ end
72
+ it "throws a PromisedKeysNotInContextError" do
73
+ # FooAction invoked with nothing placed in the context
74
+ expect { FooNoPromisedKeyAction.execute(:baz => 3) }.to \
75
+ raise_error(FunctionalLightService::PromisedKeysNotInContextError)
76
+ end
77
+ end
78
+
79
+ describe "promised keys" do
80
+ class FooWithExpectsAndPromisesAction
81
+ extend FunctionalLightService::Action
82
+
83
+ expects :baz
84
+ promises :bar
85
+
86
+ executed do |context|
87
+ # Notice how I use `context.bar` here
88
+ context.bar = context.baz + 2
89
+ end
90
+ end
91
+ it "puts the value through the accessor into the context" do
92
+ result = FooWithExpectsAndPromisesAction.execute(:baz => 3)
93
+ expect(result).to be_success
94
+ expect(result[:bar]).to eq(5)
95
+ end
96
+ end
97
+ end
@@ -1,122 +1,126 @@
1
- require 'spec_helper'
2
- require 'test_doubles'
3
-
4
- describe ":promises macro" do
5
- context "when the promised key is not in the context" do
6
- it "raises an ArgumentError" do
7
- module TestDoubles
8
- class MakesCappuccinoAction1
9
- extend FunctionalLightService::Action
10
- expects :coffee, :milk
11
- promises :cappuccino
12
- executed do |context|
13
- context[:macchiato] = "#{context.coffee} - #{context.milk}"
14
- end
15
- end
16
- end
17
-
18
- exception_msg = "promised :cappuccino to be in the context during " \
19
- "TestDoubles::MakesCappuccinoAction1"
20
- expect do
21
- TestDoubles::MakesCappuccinoAction1.execute(:coffee => "espresso",
22
- :milk => "2%")
23
- end.to \
24
- raise_error(FunctionalLightService::PromisedKeysNotInContextError, exception_msg)
25
- end
26
-
27
- it "can fail the context without fulfilling its promise" do
28
- module TestDoubles
29
- class MakesCappuccinoAction2
30
- extend FunctionalLightService::Action
31
- expects :coffee, :milk
32
- promises :cappuccino
33
- executed do |context|
34
- context.fail!("Sorry, something bad has happened.")
35
- end
36
- end
37
- end
38
-
39
- result_context = TestDoubles::MakesCappuccinoAction2
40
- .execute(:coffee => "espresso",
41
- :milk => "2%")
42
-
43
- expect(result_context).to be_failure
44
- expect(result_context.keys).not_to include(:cappuccino)
45
- end
46
- end
47
-
48
- context "when the promised key is in the context" do
49
- it "can be set with an actual value" do
50
- module TestDoubles
51
- class MakesCappuccinoAction3
52
- extend FunctionalLightService::Action
53
- expects :coffee, :milk
54
- promises :cappuccino
55
- executed do |context|
56
- context.cappuccino = "#{context.coffee} - with #{context.milk} milk"
57
- context.cappuccino += " hot"
58
- end
59
- end
60
- end
61
-
62
- result_context = TestDoubles::MakesCappuccinoAction3
63
- .execute(:coffee => "espresso",
64
- :milk => "2%")
65
-
66
- expect(result_context).to be_success
67
- expect(result_context.cappuccino).to eq("espresso - with 2% milk hot")
68
- end
69
-
70
- it "can be set with nil" do
71
- module TestDoubles
72
- class MakesCappuccinoAction4
73
- extend FunctionalLightService::Action
74
- expects :coffee, :milk
75
- promises :cappuccino
76
- executed do |context|
77
- context.cappuccino = nil
78
- end
79
- end
80
- end
81
- result_context = TestDoubles::MakesCappuccinoAction4
82
- .execute(:coffee => "espresso",
83
- :milk => "2%")
84
-
85
- expect(result_context).to be_success
86
- expect(result_context[:cappuccino]).to be_nil
87
- end
88
- end
89
-
90
- context "when a reserved key is listed as a promised key" do
91
- it "raises error indicating a reserved key has been promised" do
92
- exception_msg = "promised or expected keys cannot be a reserved key: "\
93
- "[:message]"
94
- expect do
95
- TestDoubles::MakesTeaPromisingReservedKey.execute(:tea => "black")
96
- end.to \
97
- raise_error(FunctionalLightService::ReservedKeysInContextError, exception_msg)
98
- end
99
-
100
- it "raises error indicating multiple reserved keys have been promised" do
101
- exception_msg = "promised or expected keys cannot be a reserved key: " \
102
- "[:message, :error_code, :current_action]"
103
- expect do
104
- ctx = { :tea => "black" }
105
- TestDoubles::MakesTeaPromisingMultipleReservedKeys.execute(ctx)
106
- end.to \
107
- raise_error(FunctionalLightService::ReservedKeysInContextError, exception_msg)
108
- end
109
- end
110
-
111
- context "when the `promised` macro is called multiple times" do
112
- it "collects promised keys " do
113
- result = TestDoubles::MultiplePromisesAction \
114
- .execute(:coffee => "espresso", :milk => "2%")
115
-
116
- expect(result.cappuccino).to \
117
- eq("Cappucino needs espresso and a little milk")
118
- expect(result.latte).to \
119
- eq("Latte needs espresso and a lot of milk")
120
- end
121
- end
122
- end
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ describe ":promises macro" do
5
+ context "when the promised key is not in the context" do
6
+ it "raises an ArgumentError" do
7
+ module TestDoubles
8
+ class MakesCappuccinoAction1
9
+ extend FunctionalLightService::Action
10
+
11
+ expects :coffee, :milk
12
+ promises :cappuccino
13
+ executed do |context|
14
+ context[:macchiato] = "#{context.coffee} - #{context.milk}"
15
+ end
16
+ end
17
+ end
18
+
19
+ exception_msg = "promised :cappuccino to be in the context during " \
20
+ "TestDoubles::MakesCappuccinoAction1"
21
+ expect do
22
+ TestDoubles::MakesCappuccinoAction1.execute(:coffee => "espresso",
23
+ :milk => "2%")
24
+ end.to \
25
+ raise_error(FunctionalLightService::PromisedKeysNotInContextError, exception_msg)
26
+ end
27
+
28
+ it "can fail the context without fulfilling its promise" do
29
+ module TestDoubles
30
+ class MakesCappuccinoAction2
31
+ extend FunctionalLightService::Action
32
+
33
+ expects :coffee, :milk
34
+ promises :cappuccino
35
+ executed do |context|
36
+ context.fail!("Sorry, something bad has happened.")
37
+ end
38
+ end
39
+ end
40
+
41
+ result_context = TestDoubles::MakesCappuccinoAction2
42
+ .execute(:coffee => "espresso",
43
+ :milk => "2%")
44
+
45
+ expect(result_context).to be_failure
46
+ expect(result_context.keys).not_to include(:cappuccino)
47
+ end
48
+ end
49
+
50
+ context "when the promised key is in the context" do
51
+ it "can be set with an actual value" do
52
+ module TestDoubles
53
+ class MakesCappuccinoAction3
54
+ extend FunctionalLightService::Action
55
+
56
+ expects :coffee, :milk
57
+ promises :cappuccino
58
+ executed do |context|
59
+ context.cappuccino = "#{context.coffee} - with #{context.milk} milk"
60
+ context.cappuccino += " hot"
61
+ end
62
+ end
63
+ end
64
+
65
+ result_context = TestDoubles::MakesCappuccinoAction3
66
+ .execute(:coffee => "espresso",
67
+ :milk => "2%")
68
+
69
+ expect(result_context).to be_success
70
+ expect(result_context.cappuccino).to eq("espresso - with 2% milk hot")
71
+ end
72
+
73
+ it "can be set with nil" do
74
+ module TestDoubles
75
+ class MakesCappuccinoAction4
76
+ extend FunctionalLightService::Action
77
+
78
+ expects :coffee, :milk
79
+ promises :cappuccino
80
+ executed do |context|
81
+ context.cappuccino = nil
82
+ end
83
+ end
84
+ end
85
+ result_context = TestDoubles::MakesCappuccinoAction4
86
+ .execute(:coffee => "espresso",
87
+ :milk => "2%")
88
+
89
+ expect(result_context).to be_success
90
+ expect(result_context[:cappuccino]).to be_nil
91
+ end
92
+ end
93
+
94
+ context "when a reserved key is listed as a promised key" do
95
+ it "raises error indicating a reserved key has been promised" do
96
+ exception_msg = "promised or expected keys cannot be a reserved key: " \
97
+ "[:message]"
98
+ expect do
99
+ TestDoubles::MakesTeaPromisingReservedKey.execute(:tea => "black")
100
+ end.to \
101
+ raise_error(FunctionalLightService::ReservedKeysInContextError, exception_msg)
102
+ end
103
+
104
+ it "raises error indicating multiple reserved keys have been promised" do
105
+ exception_msg = "promised or expected keys cannot be a reserved key: " \
106
+ "[:message, :error_code, :current_action]"
107
+ expect do
108
+ ctx = { :tea => "black" }
109
+ TestDoubles::MakesTeaPromisingMultipleReservedKeys.execute(ctx)
110
+ end.to \
111
+ raise_error(FunctionalLightService::ReservedKeysInContextError, exception_msg)
112
+ end
113
+ end
114
+
115
+ context "when the `promised` macro is called multiple times" do
116
+ it "collects promised keys " do
117
+ result = TestDoubles::MultiplePromisesAction
118
+ .execute(:coffee => "espresso", :milk => "2%")
119
+
120
+ expect(result.cappuccino).to \
121
+ eq("Cappucino needs espresso and a little milk")
122
+ expect(result.latte).to \
123
+ eq("Latte needs espresso and a lot of milk")
124
+ end
125
+ end
126
+ end