functional-light-service 0.5.4 → 6.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.
- 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 +38 -0
- data/README.md +54 -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/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 +30 -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/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 +45 -161
- data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
- data/spec/acceptance/include_warning_spec.rb +0 -29
|
@@ -1,132 +1,183 @@
|
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
context.
|
|
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
|
-
context.
|
|
94
|
-
end
|
|
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
|
-
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
end
|
|
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
|
+
|
|
19
|
+
expects :number
|
|
20
|
+
promises :number
|
|
21
|
+
|
|
22
|
+
executed do |context|
|
|
23
|
+
context.fail_with_rollback! if context.number.zero?
|
|
24
|
+
|
|
25
|
+
context.number += 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
rolled_back do |context|
|
|
29
|
+
context.number -= 1
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class AddsThreeWithRollbackAction
|
|
34
|
+
extend FunctionalLightService::Action
|
|
35
|
+
|
|
36
|
+
expects :number
|
|
37
|
+
|
|
38
|
+
executed do |context|
|
|
39
|
+
context.number = context.number + 3
|
|
40
|
+
|
|
41
|
+
context.fail_with_rollback!("I did not like this!")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
rolled_back do |context|
|
|
45
|
+
context.number -= 3
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class RollbackOrganizerWithNoRollback
|
|
50
|
+
extend FunctionalLightService::Organizer
|
|
51
|
+
|
|
52
|
+
def self.call(number)
|
|
53
|
+
with(:number => number).reduce(
|
|
54
|
+
TestDoubles::AddsOneAction,
|
|
55
|
+
TestDoubles::AddsTwoAction,
|
|
56
|
+
AddsThreeWithNoRollbackAction
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class AddsThreeWithNoRollbackAction
|
|
62
|
+
extend FunctionalLightService::Action
|
|
63
|
+
|
|
64
|
+
expects :number
|
|
65
|
+
|
|
66
|
+
executed do |context|
|
|
67
|
+
context.number = context.number + 3
|
|
68
|
+
|
|
69
|
+
context.fail_with_rollback!("I did not like this!")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class RollbackOrganizerWithMiddleRollback
|
|
74
|
+
extend FunctionalLightService::Organizer
|
|
75
|
+
|
|
76
|
+
def self.call(number)
|
|
77
|
+
with(:number => number).reduce(
|
|
78
|
+
TestDoubles::AddsOneAction,
|
|
79
|
+
AddsTwoActionWithRollback,
|
|
80
|
+
TestDoubles::AddsThreeAction
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class AddsTwoActionWithRollback
|
|
86
|
+
extend FunctionalLightService::Action
|
|
87
|
+
|
|
88
|
+
expects :number
|
|
89
|
+
|
|
90
|
+
executed do |context|
|
|
91
|
+
context.number = context.number + 2
|
|
92
|
+
|
|
93
|
+
context.fail_with_rollback!("I did not like this a bit!")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
rolled_back do |context|
|
|
97
|
+
context.number -= 2
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class RollbackOrganizerWithDuplicatedAction
|
|
102
|
+
extend FunctionalLightService::Organizer
|
|
103
|
+
|
|
104
|
+
def self.call(ctx)
|
|
105
|
+
with(ctx).reduce(
|
|
106
|
+
TracksRollbackAction,
|
|
107
|
+
FailsOnSecondRunWithRollbackAction,
|
|
108
|
+
TracksRollbackAction,
|
|
109
|
+
FailsOnSecondRunWithRollbackAction
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class TracksRollbackAction
|
|
115
|
+
extend FunctionalLightService::Action
|
|
116
|
+
|
|
117
|
+
executed do |context|
|
|
118
|
+
(context[:executed] ||= []) << :tracks
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
rolled_back do |context|
|
|
122
|
+
(context[:rolled_back] ||= []) << :tracks
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class FailsOnSecondRunWithRollbackAction
|
|
127
|
+
extend FunctionalLightService::Action
|
|
128
|
+
|
|
129
|
+
executed do |context|
|
|
130
|
+
(context[:executed] ||= []) << :fails
|
|
131
|
+
context.fail_with_rollback!("boom") if context[:executed].count(:fails) == 2
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
rolled_back do |context|
|
|
135
|
+
(context[:rolled_back] ||= []) << :fails
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
describe "Rolling back actions when there is a failure" do
|
|
140
|
+
it "Adds 1, 2, 3 to 1 and rolls back " do
|
|
141
|
+
result = RollbackOrganizer.call 1
|
|
142
|
+
number = result.fetch(:number)
|
|
143
|
+
|
|
144
|
+
expect(result).to be_failure
|
|
145
|
+
expect(result.message).to eq("I did not like this!")
|
|
146
|
+
expect(number).to eq(3)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "won't error out when actions don't define rollback" do
|
|
150
|
+
result = RollbackOrganizerWithNoRollback.call 1
|
|
151
|
+
number = result.fetch(:number)
|
|
152
|
+
|
|
153
|
+
expect(result).to be_failure
|
|
154
|
+
expect(result.message).to eq("I did not like this!")
|
|
155
|
+
expect(number).to eq(7)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "rolls back properly when triggered with an action in the middle" do
|
|
159
|
+
result = RollbackOrganizerWithMiddleRollback.call 1
|
|
160
|
+
number = result.fetch(:number)
|
|
161
|
+
|
|
162
|
+
expect(result).to be_failure
|
|
163
|
+
expect(result.message).to eq("I did not like this a bit!")
|
|
164
|
+
expect(number).to eq(2)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "rolls back from the first action" do
|
|
168
|
+
result = RollbackOrganizer.call 0
|
|
169
|
+
number = result.fetch(:number)
|
|
170
|
+
|
|
171
|
+
expect(result).to be_failure
|
|
172
|
+
expect(number).to eq(-1)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "rolls back every executed action when the same action appears twice" do
|
|
176
|
+
result = RollbackOrganizerWithDuplicatedAction.call({})
|
|
177
|
+
|
|
178
|
+
expect(result).to be_failure
|
|
179
|
+
expect(result[:executed]).to eq(%i[tracks fails tracks fails])
|
|
180
|
+
# tutte e 4 le azioni eseguite vengono compensate, in ordine inverso
|
|
181
|
+
expect(result[:rolled_back]).to eq(%i[fails tracks fails tracks])
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -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
|