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.
Files changed (75) 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 +38 -0
  6. data/README.md +54 -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/localization_adapter.rb +48 -47
  23. data/lib/functional-light-service/organizer/execute.rb +16 -14
  24. data/lib/functional-light-service/organizer/iterate.rb +30 -25
  25. data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
  26. data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
  27. data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
  28. data/lib/functional-light-service/organizer/with_callback.rb +28 -26
  29. data/lib/functional-light-service/organizer/with_reducer.rb +81 -77
  30. data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
  31. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -108
  32. data/lib/functional-light-service/organizer.rb +114 -114
  33. data/lib/functional-light-service/testing/context_factory.rb +48 -42
  34. data/lib/functional-light-service/testing.rb +3 -1
  35. data/lib/functional-light-service/version.rb +5 -3
  36. data/lib/functional-light-service.rb +30 -28
  37. data/spec/acceptance/after_actions_spec.rb +87 -71
  38. data/spec/acceptance/before_actions_spec.rb +115 -98
  39. data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
  40. data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
  41. data/spec/acceptance/fail_spec.rb +52 -50
  42. data/spec/acceptance/message_localization_spec.rb +119 -118
  43. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
  44. data/spec/acceptance/organizer/reduce_if_spec.rb +89 -89
  45. data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
  46. data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
  47. data/spec/acceptance/rollback_spec.rb +183 -132
  48. data/spec/action_expects_and_promises_spec.rb +97 -93
  49. data/spec/action_promised_keys_spec.rb +126 -122
  50. data/spec/context_spec.rb +289 -197
  51. data/spec/examples/controller_spec.rb +63 -63
  52. data/spec/examples/validate_address_spec.rb +38 -37
  53. data/spec/lib/deterministic/currify_spec.rb +90 -88
  54. data/spec/lib/deterministic/null_spec.rb +6 -1
  55. data/spec/lib/deterministic/option_spec.rb +140 -137
  56. data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
  57. data/spec/lib/deterministic/result/result_shared.rb +3 -2
  58. data/spec/lib/deterministic/result_spec.rb +2 -2
  59. data/spec/lib/edge_cases_spec.rb +156 -0
  60. data/spec/lib/enum_spec.rb +1 -1
  61. data/spec/lib/native_pattern_matching_spec.rb +74 -0
  62. data/spec/organizer_spec.rb +115 -114
  63. data/spec/readme_spec.rb +45 -47
  64. data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
  65. data/spec/sample/calculates_tax_spec.rb +1 -1
  66. data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
  67. data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
  68. data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
  69. data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
  70. data/spec/spec_helper.rb +6 -0
  71. data/spec/test_doubles.rb +628 -599
  72. data/spec/testing/context_factory_spec.rb +21 -0
  73. metadata +45 -161
  74. data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
  75. 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
- 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
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
- 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