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.
- checksums.yaml +4 -4
- data/.github/workflows/project-build.yml +35 -11
- data/.rubocop.yml +101 -160
- data/AUDIT-functional-light-service.md +352 -0
- data/CHANGELOG.md +45 -0
- data/README.md +88 -2
- data/audit/bench.rb +99 -0
- data/audit/verify_findings.rb +172 -0
- data/functional-light-service.gemspec +15 -21
- data/lib/functional-light-service/action.rb +97 -101
- data/lib/functional-light-service/configuration.rb +26 -24
- data/lib/functional-light-service/context/key_verifier.rb +124 -118
- data/lib/functional-light-service/context.rb +63 -20
- data/lib/functional-light-service/deprecations.rb +26 -0
- data/lib/functional-light-service/errors.rb +8 -6
- data/lib/functional-light-service/functional/enum.rb +286 -250
- data/lib/functional-light-service/functional/maybe.rb +21 -15
- data/lib/functional-light-service/functional/monad.rb +77 -66
- data/lib/functional-light-service/functional/null.rb +88 -74
- data/lib/functional-light-service/functional/option.rb +100 -97
- data/lib/functional-light-service/functional/result.rb +129 -116
- data/lib/functional-light-service/functional/sequencer.rb +144 -0
- data/lib/functional-light-service/localization_adapter.rb +48 -47
- data/lib/functional-light-service/organizer/execute.rb +16 -14
- data/lib/functional-light-service/organizer/iterate.rb +30 -25
- data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
- data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
- data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
- data/lib/functional-light-service/organizer/with_callback.rb +28 -26
- data/lib/functional-light-service/organizer/with_reducer.rb +81 -77
- data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
- data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -108
- data/lib/functional-light-service/organizer.rb +114 -114
- data/lib/functional-light-service/testing/context_factory.rb +48 -42
- data/lib/functional-light-service/testing.rb +3 -1
- data/lib/functional-light-service/version.rb +5 -3
- data/lib/functional-light-service.rb +31 -28
- data/spec/acceptance/after_actions_spec.rb +87 -71
- data/spec/acceptance/before_actions_spec.rb +115 -98
- data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
- data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
- data/spec/acceptance/fail_spec.rb +52 -50
- data/spec/acceptance/message_localization_spec.rb +119 -118
- data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
- data/spec/acceptance/organizer/reduce_if_spec.rb +89 -89
- data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
- data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
- data/spec/acceptance/rollback_spec.rb +183 -132
- data/spec/action_expects_and_promises_spec.rb +97 -93
- data/spec/action_promised_keys_spec.rb +126 -122
- data/spec/context_spec.rb +289 -197
- data/spec/examples/controller_spec.rb +63 -63
- data/spec/examples/validate_address_spec.rb +38 -37
- data/spec/lib/deterministic/currify_spec.rb +90 -88
- data/spec/lib/deterministic/null_spec.rb +6 -1
- data/spec/lib/deterministic/option_spec.rb +140 -137
- data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
- data/spec/lib/deterministic/result/result_shared.rb +3 -2
- data/spec/lib/deterministic/result_spec.rb +2 -2
- data/spec/lib/deterministic/sequencer_spec.rb +506 -0
- data/spec/lib/edge_cases_spec.rb +156 -0
- data/spec/lib/enum_spec.rb +1 -1
- data/spec/lib/native_pattern_matching_spec.rb +74 -0
- data/spec/organizer_spec.rb +115 -114
- data/spec/readme_spec.rb +45 -47
- data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
- data/spec/sample/calculates_tax_spec.rb +1 -1
- data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
- data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
- data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
- data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
- data/spec/spec_helper.rb +6 -0
- data/spec/test_doubles.rb +628 -599
- data/spec/testing/context_factory_spec.rb +21 -0
- metadata +48 -162
- data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
- 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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
context
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|