functional-light-service 0.4.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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/project-build.yml +43 -11
  3. data/.rubocop.yml +101 -160
  4. data/AUDIT-functional-light-service.md +352 -0
  5. data/Appraisals +4 -0
  6. data/CHANGELOG.md +118 -0
  7. data/Gemfile +0 -2
  8. data/README.md +1544 -1426
  9. data/Rakefile +1 -1
  10. data/VERSION +1 -1
  11. data/audit/bench.rb +99 -0
  12. data/audit/verify_findings.rb +172 -0
  13. data/functional-light-service.gemspec +15 -16
  14. data/lib/functional-light-service/action.rb +97 -101
  15. data/lib/functional-light-service/configuration.rb +26 -24
  16. data/lib/functional-light-service/context/key_verifier.rb +124 -118
  17. data/lib/functional-light-service/context.rb +63 -20
  18. data/lib/functional-light-service/deprecations.rb +26 -0
  19. data/lib/functional-light-service/errors.rb +8 -6
  20. data/lib/functional-light-service/functional/enum.rb +286 -250
  21. data/lib/functional-light-service/functional/maybe.rb +21 -15
  22. data/lib/functional-light-service/functional/monad.rb +77 -66
  23. data/lib/functional-light-service/functional/null.rb +88 -74
  24. data/lib/functional-light-service/functional/option.rb +100 -97
  25. data/lib/functional-light-service/functional/result.rb +129 -116
  26. data/lib/functional-light-service/localization_adapter.rb +48 -47
  27. data/lib/functional-light-service/organizer/execute.rb +16 -14
  28. data/lib/functional-light-service/organizer/iterate.rb +30 -25
  29. data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
  30. data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
  31. data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
  32. data/lib/functional-light-service/organizer/with_callback.rb +28 -26
  33. data/lib/functional-light-service/organizer/with_reducer.rb +81 -71
  34. data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
  35. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -105
  36. data/lib/functional-light-service/organizer.rb +114 -104
  37. data/lib/functional-light-service/testing/context_factory.rb +48 -42
  38. data/lib/functional-light-service/testing.rb +3 -1
  39. data/lib/functional-light-service/version.rb +5 -3
  40. data/lib/functional-light-service.rb +30 -28
  41. data/spec/acceptance/after_actions_spec.rb +87 -71
  42. data/spec/acceptance/before_actions_spec.rb +115 -98
  43. data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
  44. data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
  45. data/spec/acceptance/fail_spec.rb +52 -50
  46. data/spec/acceptance/message_localization_spec.rb +119 -118
  47. data/spec/acceptance/organizer/add_aliases_spec.rb +28 -0
  48. data/spec/acceptance/organizer/add_to_context_spec.rb +30 -0
  49. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
  50. data/spec/acceptance/organizer/iterate_spec.rb +7 -0
  51. data/spec/acceptance/organizer/reduce_if_spec.rb +89 -83
  52. data/spec/acceptance/organizer/reduce_until_spec.rb +6 -0
  53. data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
  54. data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
  55. data/spec/acceptance/rollback_spec.rb +183 -132
  56. data/spec/action_expects_and_promises_spec.rb +97 -93
  57. data/spec/action_promised_keys_spec.rb +126 -122
  58. data/spec/action_spec.rb +8 -0
  59. data/spec/context_spec.rb +289 -197
  60. data/spec/examples/controller_spec.rb +63 -63
  61. data/spec/examples/validate_address_spec.rb +38 -37
  62. data/spec/lib/deterministic/currify_spec.rb +90 -88
  63. data/spec/lib/deterministic/null_spec.rb +6 -1
  64. data/spec/lib/deterministic/option_spec.rb +140 -133
  65. data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
  66. data/spec/lib/deterministic/result/result_shared.rb +3 -2
  67. data/spec/lib/deterministic/result_spec.rb +2 -2
  68. data/spec/lib/edge_cases_spec.rb +156 -0
  69. data/spec/lib/enum_spec.rb +1 -1
  70. data/spec/lib/native_pattern_matching_spec.rb +74 -0
  71. data/spec/organizer_spec.rb +115 -93
  72. data/spec/readme_spec.rb +45 -47
  73. data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
  74. data/spec/sample/calculates_tax_spec.rb +1 -1
  75. data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
  76. data/spec/sample/provides_free_shipping_action_spec.rb +1 -1
  77. data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
  78. data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
  79. data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
  80. data/spec/spec_helper.rb +21 -13
  81. data/spec/test_doubles.rb +628 -564
  82. data/spec/testing/context_factory_spec.rb +21 -0
  83. metadata +49 -117
  84. data/.travis.yml +0 -24
  85. data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
  86. data/spec/acceptance/include_warning_spec.rb +0 -29
data/spec/test_doubles.rb CHANGED
@@ -1,564 +1,628 @@
1
- # A collection of Action and Organizer dummies used in specs
2
-
3
- module TestDoubles
4
- class RollbackAction
5
- extend FunctionalLightService::Action
6
- executed(&:fail_with_rollback!)
7
- end
8
-
9
- class RaiseErrorAction
10
- extend FunctionalLightService::Action
11
- executed do |_ctx|
12
- raise 'A problem has occured.'
13
- end
14
- end
15
-
16
- class RaiseAnotherErrorAction
17
- extend FunctionalLightService::Action
18
- executed do |_ctx|
19
- raise 'More problems'
20
- end
21
- end
22
-
23
- class SkipAllAction
24
- extend FunctionalLightService::Action
25
- executed(&:skip_remaining!)
26
- end
27
-
28
- class FailureAction
29
- extend FunctionalLightService::Action
30
- executed(&:fail!)
31
- end
32
-
33
- class AddTwoOrganizer
34
- extend FunctionalLightService::Organizer
35
- def self.call(context)
36
- with(context).reduce([AddsOneAction, AddsOneAction])
37
- end
38
- end
39
-
40
- class AroundEachNullHandler
41
- def self.call(_action)
42
- yield
43
- end
44
- end
45
-
46
- class TestLogger
47
- attr_accessor :logs
48
-
49
- def initialize
50
- @logs = []
51
- end
52
- end
53
-
54
- class AroundEachLoggerHandler
55
- def self.call(context)
56
- before_number = context[:number]
57
- result = yield
58
-
59
- context[:logger].logs << {
60
- :action => context.current_action,
61
- :before => before_number,
62
- :after => result[:number]
63
- }
64
-
65
- result
66
- end
67
- end
68
-
69
- class AroundEachOrganizer
70
- extend FunctionalLightService::Organizer
71
- def self.call(action_arguments)
72
- with(action_arguments)
73
- .around_each(AroundEachLoggerHandler)
74
- .reduce([AddsTwoActionWithFetch])
75
- end
76
- end
77
-
78
- class AddsTwoActionWithFetch
79
- extend FunctionalLightService::Action
80
-
81
- executed do |context|
82
- number = context.fetch(:number, 0)
83
- context[:number] = number + 2
84
- end
85
- end
86
-
87
- class AnAction
88
- extend FunctionalLightService::Action
89
- end
90
-
91
- class AnotherAction
92
- extend FunctionalLightService::Action
93
- end
94
-
95
- class AnOrganizer
96
- extend FunctionalLightService::Organizer
97
-
98
- def self.call(action_arguments)
99
- with(action_arguments).reduce([AnAction, AnotherAction])
100
- end
101
-
102
- def self.do_something_with_no_actions(action_arguments)
103
- with(action_arguments).reduce
104
- end
105
-
106
- def self.do_something_with_no_starting_context
107
- reduce([AnAction, AnotherAction])
108
- end
109
- end
110
-
111
- class NotExplicitlyReturningContextOrganizer
112
- extend FunctionalLightService::Organizer
113
-
114
- def self.call(context)
115
- context[:foo] = [1, 2, 3]
116
- end
117
- end
118
-
119
- class NestingOrganizer
120
- extend FunctionalLightService::Organizer
121
-
122
- def self.call(context)
123
- with(context).reduce(actions)
124
- end
125
-
126
- def self.actions
127
- [NotExplicitlyReturningContextOrganizer, NestedAction]
128
- end
129
- end
130
-
131
- class NestedAction
132
- extend FunctionalLightService::Action
133
-
134
- expects :foo
135
-
136
- executed do |context|
137
- context[:bar] = context.foo
138
- end
139
- end
140
-
141
- class MakesTeaWithMilkAction
142
- extend FunctionalLightService::Action
143
- expects :tea, :milk
144
- promises :milk_tea
145
-
146
- executed do |context|
147
- context.milk_tea = "#{context.tea} - #{context.milk}"
148
- end
149
- end
150
-
151
- class MultipleExpectsAction
152
- extend FunctionalLightService::Action
153
- expects :tea
154
- expects :milk, :chocolate
155
- promises :milk_tea
156
-
157
- executed do |context|
158
- context.milk_tea = "#{context.tea} - #{context.milk}"\
159
- " - with #{context.chocolate}"
160
- end
161
- end
162
-
163
- class MakesCappuccinoAction
164
- extend FunctionalLightService::Action
165
- expects :coffee, :milk
166
- promises :cappuccino
167
- end
168
-
169
- class MakesLatteAction
170
- extend FunctionalLightService::Action
171
- expects :coffee, :milk
172
- promises :latte
173
-
174
- executed do |context|
175
- context.fail!("Can't make a latte from a milk that's very hot!") if context.milk == :very_hot
176
-
177
- if context.milk == :super_hot
178
- error_message = "Can't make a latte from a milk that's super hot!"
179
- context.fail_with_rollback!(error_message)
180
- end
181
-
182
- context[:latte] = "#{context.coffee} - with lots of #{context.milk}"
183
-
184
- if context.milk == "5%"
185
- msg = "Can't make a latte with a fatty milk like that!"
186
- context.skip_remaining!(msg)
187
- next context
188
- end
189
- end
190
- end
191
-
192
- class MultiplePromisesAction
193
- extend FunctionalLightService::Action
194
- expects :coffee, :milk
195
- promises :cappuccino
196
- promises :latte
197
-
198
- executed do |context|
199
- context.cappuccino = "Cappucino needs #{context.coffee} and a little milk"
200
- context.latte = "Latte needs #{context.coffee} and a lot of milk"
201
- end
202
- end
203
-
204
- class MakesTeaAndCappuccino
205
- extend FunctionalLightService::Organizer
206
-
207
- def self.call(tea, milk, coffee)
208
- with(:tea => tea, :milk => milk, :coffee => coffee)
209
- .reduce(TestDoubles::MakesTeaWithMilkAction,
210
- TestDoubles::MakesLatteAction)
211
- end
212
- end
213
-
214
- class MakesCappuccinoAddsTwo
215
- extend FunctionalLightService::Organizer
216
-
217
- def self.call(milk, coffee)
218
- with(:milk => milk, :coffee => coffee)
219
- .reduce(TestDoubles::AddsTwoActionWithFetch,
220
- TestDoubles::MakesLatteAction)
221
- end
222
- end
223
-
224
- class MakesCappuccinoAddsTwoAndFails
225
- extend FunctionalLightService::Organizer
226
-
227
- def self.call(coffee, this_hot = :very_hot)
228
- with(:milk => this_hot, :coffee => coffee)
229
- .reduce(TestDoubles::MakesLatteAction,
230
- TestDoubles::AddsTwoActionWithFetch)
231
- end
232
- end
233
-
234
- class MakesCappuccinoSkipsAddsTwo
235
- extend FunctionalLightService::Organizer
236
-
237
- def self.call(coffee)
238
- with(:milk => "5%", :coffee => coffee)
239
- .reduce(TestDoubles::MakesLatteAction,
240
- TestDoubles::AddsTwoActionWithFetch)
241
- end
242
- end
243
-
244
- class AdditionOrganizer
245
- extend FunctionalLightService::Organizer
246
-
247
- def self.call(number)
248
- with(:number => number).reduce(actions)
249
- end
250
-
251
- def self.actions
252
- [
253
- AddsOneAction,
254
- AddsTwoAction,
255
- AddsThreeAction
256
- ]
257
- end
258
- end
259
-
260
- class ExtraArgumentAdditionOrganizer
261
- extend FunctionalLightService::Organizer
262
-
263
- def self.call(number, another_number)
264
- with(:number => number + another_number).reduce(actions)
265
- end
266
-
267
- def self.actions
268
- [
269
- AddsOneAction,
270
- AddsTwoAction,
271
- AddsThreeAction
272
- ]
273
- end
274
- end
275
-
276
- class AddsOne
277
- extend FunctionalLightService::Organizer
278
-
279
- def call(ctx)
280
- with(ctx).reduce(actions)
281
- end
282
-
283
- def self.actions
284
- [AddsOneAction]
285
- end
286
- end
287
-
288
- class AddsOneAction
289
- extend FunctionalLightService::Action
290
- expects :number
291
- promises :number
292
-
293
- executed do |context|
294
- context.number += 1
295
- end
296
- end
297
-
298
- class AddsTwoAction
299
- extend FunctionalLightService::Action
300
- expects :number
301
-
302
- executed do |context|
303
- context.number += 2
304
- end
305
- end
306
-
307
- class AddsThreeAction
308
- extend FunctionalLightService::Action
309
- expects :number
310
-
311
- executed do |context|
312
- context.number += 3
313
- end
314
- end
315
-
316
- class IterateOrganizer
317
- extend FunctionalLightService::Organizer
318
-
319
- def self.call(ctx)
320
- with(ctx).reduce(actions)
321
- end
322
-
323
- def self.actions
324
- [
325
- AddsOneIteratesAction,
326
- iterate(:numbers, [
327
- AddsTwoAction,
328
- AddsThreeAction
329
- ])
330
- ]
331
- end
332
- end
333
-
334
- class AddsOneIteratesAction
335
- extend FunctionalLightService::Action
336
- expects :numbers
337
- promises :numbers
338
-
339
- executed do |context|
340
- context.numbers = context.numbers.map { |n| n + 1 }
341
- end
342
- end
343
-
344
- class CallbackOrganizer
345
- extend FunctionalLightService::Organizer
346
-
347
- def self.call(ctx)
348
- with(ctx).reduce(actions)
349
- end
350
-
351
- def self.actions
352
- [
353
- AddsOneAction,
354
- with_callback(AddTenCallbackAction, [
355
- AddsTwoAction,
356
- AddsThreeAction
357
- ])
358
- ]
359
- end
360
- end
361
-
362
- class AddTenCallbackAction
363
- extend FunctionalLightService::Action
364
- expects :number, :callback
365
-
366
- executed do |context|
367
- context.number += 10
368
- context.number =
369
- context.callback.call(context).fetch(:number)
370
- end
371
- end
372
-
373
- class ReduceUntilOrganizer
374
- extend FunctionalLightService::Organizer
375
-
376
- def self.call(ctx)
377
- with(ctx).reduce(actions)
378
- end
379
-
380
- def self.actions
381
- [
382
- AddsOneAction,
383
- reduce_until(->(ctx) { ctx.number > 3 }, [
384
- AddsTwoAction,
385
- AddsThreeAction
386
- ])
387
- ]
388
- end
389
- end
390
-
391
- class ReduceIfOrganizer
392
- extend FunctionalLightService::Organizer
393
-
394
- def self.call(ctx)
395
- with(ctx).reduce(actions)
396
- end
397
-
398
- def self.actions
399
- [
400
- AddsOneAction,
401
- reduce_if(->(ctx) { ctx.number > 1 }, [
402
- AddsTwoAction,
403
- AddsThreeAction
404
- ])
405
- ]
406
- end
407
- end
408
-
409
- class MakesTeaExpectingReservedKey
410
- extend FunctionalLightService::Action
411
- expects :tea, :message
412
-
413
- executed do |context|
414
- context.product = context.number + 3
415
- end
416
- end
417
-
418
- class MakesTeaExpectingMultipleReservedKeys
419
- extend FunctionalLightService::Action
420
- expects :tea, :message, :error_code, :current_action
421
-
422
- executed do |context|
423
- context.product = context.number + 3
424
- end
425
- end
426
-
427
- class MakesTeaPromisingReservedKey
428
- extend FunctionalLightService::Action
429
- expects :tea
430
- promises :product, :message
431
-
432
- executed do |context|
433
- context.product = context.number + 3
434
- end
435
- end
436
-
437
- class MakesTeaPromisingMultipleReservedKeys
438
- extend FunctionalLightService::Action
439
- expects :tea
440
- promises :product, :message, :error_code, :current_action
441
-
442
- executed do |context|
443
- context.product = context.number + 3
444
- end
445
- end
446
-
447
- class MakesTeaPromisingKeyButRaisesException
448
- extend FunctionalLightService::Action
449
- promises :product
450
-
451
- executed do |context|
452
- context.product = make_product
453
- end
454
-
455
- def self.make_product
456
- raise "Fail"
457
- end
458
- private_class_method :make_product
459
- end
460
-
461
- class PromisesPromisedKeyAction
462
- extend FunctionalLightService::Action
463
-
464
- promises :promised_key
465
-
466
- executed do |ctx|
467
- ctx.promised_key = "promised_key"
468
- end
469
- end
470
-
471
- class ExpectsExpectedKeyAction
472
- extend FunctionalLightService::Action
473
-
474
- expects :expected_key
475
- promises :final_key
476
-
477
- executed do |ctx|
478
- ctx.final_key = ctx.expected_key
479
- end
480
- end
481
-
482
- class NullAction
483
- extend FunctionalLightService::Action
484
-
485
- # rubocop:disable Lint/EmptyBlock
486
- executed { |_ctx| }
487
- # rubocop:enable Lint/EmptyBlock
488
- end
489
-
490
- class TestIterate
491
- extend FunctionalLightService::Organizer
492
-
493
- def self.call(context)
494
- with(context)
495
- .reduce([iterate(:counters,
496
- [TestDoubles::AddsOneAction])])
497
- end
498
-
499
- def self.call_single(context)
500
- with(context)
501
- .reduce([iterate(:counters,
502
- TestDoubles::AddsOneAction)])
503
- end
504
- end
505
-
506
- class TestWithCallback
507
- extend FunctionalLightService::Organizer
508
-
509
- def self.call(context = {})
510
- with(context).reduce(actions)
511
- end
512
-
513
- def self.actions
514
- [
515
- SetUpContextAction,
516
- with_callback(IterateCollectionAction,
517
- [IncrementCountAction,
518
- AddToTotalAction])
519
- ]
520
- end
521
- end
522
-
523
- class SetUpContextAction
524
- extend FunctionalLightService::Action
525
- promises :numbers, :counter, :total
526
-
527
- executed do |ctx|
528
- ctx.numbers = [1, 2, 3]
529
- ctx.counter = 0
530
- ctx.total = 0
531
- end
532
- end
533
-
534
- class IterateCollectionAction
535
- extend FunctionalLightService::Action
536
- expects :numbers, :callback
537
- promises :number
538
-
539
- executed do |ctx|
540
- ctx.numbers.each do |number|
541
- ctx.number = number
542
- ctx.callback.call(ctx)
543
- end
544
- end
545
- end
546
-
547
- class IncrementCountAction
548
- extend FunctionalLightService::Action
549
- expects :counter
550
-
551
- executed do |ctx|
552
- ctx.counter = ctx.counter + 1
553
- end
554
- end
555
-
556
- class AddToTotalAction
557
- extend FunctionalLightService::Action
558
- expects :number, :total
559
-
560
- executed do |ctx|
561
- ctx.total += ctx.number
562
- end
563
- end
564
- end
1
+ # A collection of Action and Organizer dummies used in specs
2
+
3
+ module TestDoubles
4
+ class RollbackAction
5
+ extend FunctionalLightService::Action
6
+
7
+ executed(&:fail_with_rollback!)
8
+ end
9
+
10
+ class RaiseErrorAction
11
+ extend FunctionalLightService::Action
12
+
13
+ executed do |_ctx|
14
+ raise 'A problem has occured.'
15
+ end
16
+ end
17
+
18
+ class RaiseAnotherErrorAction
19
+ extend FunctionalLightService::Action
20
+
21
+ executed do |_ctx|
22
+ raise 'More problems'
23
+ end
24
+ end
25
+
26
+ class SkipAllAction
27
+ extend FunctionalLightService::Action
28
+
29
+ executed(&:skip_remaining!)
30
+ end
31
+
32
+ class FailureAction
33
+ extend FunctionalLightService::Action
34
+
35
+ executed(&:fail!)
36
+ end
37
+
38
+ class AddTwoOrganizer
39
+ extend FunctionalLightService::Organizer
40
+
41
+ def self.call(context)
42
+ with(context).reduce([AddsOneAction, AddsOneAction])
43
+ end
44
+ end
45
+
46
+ class AroundEachNullHandler
47
+ def self.call(_action)
48
+ yield
49
+ end
50
+ end
51
+
52
+ class TestLogger
53
+ attr_accessor :logs
54
+
55
+ def initialize
56
+ @logs = []
57
+ end
58
+ end
59
+
60
+ class AroundEachLoggerHandler
61
+ def self.call(context)
62
+ before_number = context[:number]
63
+ result = yield
64
+
65
+ context[:logger].logs << {
66
+ :action => context.current_action,
67
+ :before => before_number,
68
+ :after => result[:number]
69
+ }
70
+
71
+ result
72
+ end
73
+ end
74
+
75
+ class AroundEachOrganizer
76
+ extend FunctionalLightService::Organizer
77
+
78
+ def self.call(action_arguments)
79
+ with(action_arguments)
80
+ .around_each(AroundEachLoggerHandler)
81
+ .reduce([AddsTwoActionWithFetch])
82
+ end
83
+ end
84
+
85
+ class AddsTwoActionWithFetch
86
+ extend FunctionalLightService::Action
87
+
88
+ executed do |context|
89
+ number = context.fetch(:number, 0)
90
+ context[:number] = number + 2
91
+ end
92
+ end
93
+
94
+ class AnAction
95
+ extend FunctionalLightService::Action
96
+ end
97
+
98
+ class AnotherAction
99
+ extend FunctionalLightService::Action
100
+ end
101
+
102
+ class AnOrganizer
103
+ extend FunctionalLightService::Organizer
104
+
105
+ def self.call(action_arguments)
106
+ with(action_arguments).reduce([AnAction, AnotherAction])
107
+ end
108
+
109
+ def self.do_something_with_no_actions(action_arguments)
110
+ with(action_arguments).reduce
111
+ end
112
+
113
+ def self.do_something_with_no_starting_context
114
+ reduce([AnAction, AnotherAction])
115
+ end
116
+ end
117
+
118
+ class NotExplicitlyReturningContextOrganizer
119
+ extend FunctionalLightService::Organizer
120
+
121
+ def self.call(context)
122
+ context[:foo] = [1, 2, 3]
123
+ end
124
+ end
125
+
126
+ class NestingOrganizer
127
+ extend FunctionalLightService::Organizer
128
+
129
+ def self.call(context)
130
+ with(context).reduce(actions)
131
+ end
132
+
133
+ def self.actions
134
+ [NotExplicitlyReturningContextOrganizer, NestedAction]
135
+ end
136
+ end
137
+
138
+ class NestedAction
139
+ extend FunctionalLightService::Action
140
+
141
+ expects :foo
142
+
143
+ executed do |context|
144
+ context[:bar] = context.foo
145
+ end
146
+ end
147
+
148
+ class MakesTeaWithMilkAction
149
+ extend FunctionalLightService::Action
150
+
151
+ expects :tea, :milk
152
+ promises :milk_tea
153
+
154
+ executed do |context|
155
+ context.milk_tea = "#{context.tea} - #{context.milk}"
156
+ end
157
+ end
158
+
159
+ class MultipleExpectsAction
160
+ extend FunctionalLightService::Action
161
+
162
+ expects :tea
163
+ expects :milk, :chocolate
164
+ promises :milk_tea
165
+
166
+ executed do |context|
167
+ context.milk_tea = "#{context.tea} - #{context.milk} " \
168
+ "- with #{context.chocolate}"
169
+ end
170
+ end
171
+
172
+ class MakesCappuccinoAction
173
+ extend FunctionalLightService::Action
174
+
175
+ expects :coffee, :milk
176
+ promises :cappuccino
177
+ end
178
+
179
+ class MakesLatteAction
180
+ extend FunctionalLightService::Action
181
+
182
+ expects :coffee, :milk
183
+ promises :latte
184
+
185
+ executed do |context|
186
+ context.fail!("Can't make a latte from a milk that's very hot!") if context.milk == :very_hot
187
+
188
+ if context.milk == :super_hot
189
+ error_message = "Can't make a latte from a milk that's super hot!"
190
+ context.fail_with_rollback!(error_message)
191
+ end
192
+
193
+ context[:latte] = "#{context.coffee} - with lots of #{context.milk}"
194
+
195
+ if context.milk == "5%"
196
+ msg = "Can't make a latte with a fatty milk like that!"
197
+ context.skip_remaining!(msg)
198
+ next context
199
+ end
200
+ end
201
+ end
202
+
203
+ class MultiplePromisesAction
204
+ extend FunctionalLightService::Action
205
+
206
+ expects :coffee, :milk
207
+ promises :cappuccino
208
+ promises :latte
209
+
210
+ executed do |context|
211
+ context.cappuccino = "Cappucino needs #{context.coffee} and a little milk"
212
+ context.latte = "Latte needs #{context.coffee} and a lot of milk"
213
+ end
214
+ end
215
+
216
+ class MakesTeaAndCappuccino
217
+ extend FunctionalLightService::Organizer
218
+
219
+ def self.call(tea, milk, coffee)
220
+ with(:tea => tea, :milk => milk, :coffee => coffee)
221
+ .reduce(TestDoubles::MakesTeaWithMilkAction,
222
+ TestDoubles::MakesLatteAction)
223
+ end
224
+ end
225
+
226
+ class MakesCappuccinoAddsTwo
227
+ extend FunctionalLightService::Organizer
228
+
229
+ def self.call(milk, coffee)
230
+ with(:milk => milk, :coffee => coffee)
231
+ .reduce(TestDoubles::AddsTwoActionWithFetch,
232
+ TestDoubles::MakesLatteAction)
233
+ end
234
+ end
235
+
236
+ class MakesCappuccinoAddsTwoAndFails
237
+ extend FunctionalLightService::Organizer
238
+
239
+ def self.call(coffee, this_hot = :very_hot)
240
+ with(:milk => this_hot, :coffee => coffee)
241
+ .reduce(TestDoubles::MakesLatteAction,
242
+ TestDoubles::AddsTwoActionWithFetch)
243
+ end
244
+ end
245
+
246
+ class MakesCappuccinoSkipsAddsTwo
247
+ extend FunctionalLightService::Organizer
248
+
249
+ def self.call(coffee)
250
+ with(:milk => "5%", :coffee => coffee)
251
+ .reduce(TestDoubles::MakesLatteAction,
252
+ TestDoubles::AddsTwoActionWithFetch)
253
+ end
254
+ end
255
+
256
+ class AdditionOrganizer
257
+ extend FunctionalLightService::Organizer
258
+
259
+ def self.call(number)
260
+ with(:number => number).reduce(actions)
261
+ end
262
+
263
+ def self.actions
264
+ [
265
+ AddsOneAction,
266
+ AddsTwoAction,
267
+ AddsThreeAction
268
+ ]
269
+ end
270
+ end
271
+
272
+ class ExtraArgumentAdditionOrganizer
273
+ extend FunctionalLightService::Organizer
274
+
275
+ def self.call(number, another_number)
276
+ with(:number => number + another_number).reduce(actions)
277
+ end
278
+
279
+ def self.actions
280
+ [
281
+ AddsOneAction,
282
+ AddsTwoAction,
283
+ AddsThreeAction
284
+ ]
285
+ end
286
+ end
287
+
288
+ class AddsOne
289
+ extend FunctionalLightService::Organizer
290
+
291
+ def call(ctx)
292
+ with(ctx).reduce(actions)
293
+ end
294
+
295
+ def self.actions
296
+ [AddsOneAction]
297
+ end
298
+ end
299
+
300
+ class AddsOneAction
301
+ extend FunctionalLightService::Action
302
+
303
+ expects :number
304
+ promises :number
305
+
306
+ executed do |context|
307
+ context.number += 1
308
+ end
309
+ end
310
+
311
+ class AddsTwoAction
312
+ extend FunctionalLightService::Action
313
+
314
+ expects :number
315
+
316
+ executed do |context|
317
+ context.number += 2
318
+ end
319
+ end
320
+
321
+ class AddsThreeAction
322
+ extend FunctionalLightService::Action
323
+
324
+ expects :number
325
+
326
+ executed do |context|
327
+ context.number += 3
328
+ end
329
+ end
330
+
331
+ class IterateOrganizer
332
+ extend FunctionalLightService::Organizer
333
+
334
+ def self.call(ctx)
335
+ with(ctx).reduce(actions)
336
+ end
337
+
338
+ def self.actions
339
+ [
340
+ AddsOneIteratesAction,
341
+ iterate(:numbers, [
342
+ AddsTwoAction,
343
+ AddsThreeAction
344
+ ])
345
+ ]
346
+ end
347
+ end
348
+
349
+ class AddsOneIteratesAction
350
+ extend FunctionalLightService::Action
351
+
352
+ expects :numbers
353
+ promises :numbers
354
+
355
+ executed do |context|
356
+ context.numbers = context.numbers.map { |n| n + 1 }
357
+ end
358
+ end
359
+
360
+ class CallbackOrganizer
361
+ extend FunctionalLightService::Organizer
362
+
363
+ def self.call(ctx)
364
+ with(ctx).reduce(actions)
365
+ end
366
+
367
+ def self.actions
368
+ [
369
+ AddsOneAction,
370
+ with_callback(AddTenCallbackAction, [
371
+ AddsTwoAction,
372
+ AddsThreeAction
373
+ ])
374
+ ]
375
+ end
376
+ end
377
+
378
+ class AddTenCallbackAction
379
+ extend FunctionalLightService::Action
380
+
381
+ expects :number, :callback
382
+
383
+ executed do |context|
384
+ context.number += 10
385
+ context.number =
386
+ context.callback.call(context).fetch(:number)
387
+ end
388
+ end
389
+
390
+ class ReduceUntilOrganizer
391
+ extend FunctionalLightService::Organizer
392
+
393
+ def self.call(ctx)
394
+ with(ctx).reduce(actions)
395
+ end
396
+
397
+ def self.actions
398
+ [
399
+ AddsOneAction,
400
+ reduce_until(->(ctx) { ctx.number > 3 }, [
401
+ AddsTwoAction,
402
+ AddsThreeAction
403
+ ])
404
+ ]
405
+ end
406
+ end
407
+
408
+ class ReduceIfOrganizer
409
+ extend FunctionalLightService::Organizer
410
+
411
+ def self.call(ctx)
412
+ with(ctx).reduce(actions)
413
+ end
414
+
415
+ def self.actions
416
+ [
417
+ AddsOneAction,
418
+ reduce_if(->(ctx) { ctx.number > 1 }, [
419
+ AddsTwoAction,
420
+ AddsThreeAction
421
+ ])
422
+ ]
423
+ end
424
+ end
425
+
426
+ class MakesTeaExpectingReservedKey
427
+ extend FunctionalLightService::Action
428
+
429
+ expects :tea, :message
430
+
431
+ executed do |context|
432
+ context.product = context.number + 3
433
+ end
434
+ end
435
+
436
+ class MakesTeaExpectingMultipleReservedKeys
437
+ extend FunctionalLightService::Action
438
+
439
+ expects :tea, :message, :error_code, :current_action
440
+
441
+ executed do |context|
442
+ context.product = context.number + 3
443
+ end
444
+ end
445
+
446
+ class MakesTeaPromisingReservedKey
447
+ extend FunctionalLightService::Action
448
+
449
+ expects :tea
450
+ promises :product, :message
451
+
452
+ executed do |context|
453
+ context.product = context.number + 3
454
+ end
455
+ end
456
+
457
+ class MakesTeaPromisingMultipleReservedKeys
458
+ extend FunctionalLightService::Action
459
+
460
+ expects :tea
461
+ promises :product, :message, :error_code, :current_action
462
+
463
+ executed do |context|
464
+ context.product = context.number + 3
465
+ end
466
+ end
467
+
468
+ class MakesTeaPromisingKeyButRaisesException
469
+ extend FunctionalLightService::Action
470
+
471
+ promises :product
472
+
473
+ executed do |context|
474
+ context.product = make_product
475
+ end
476
+
477
+ def self.make_product
478
+ raise "Fail"
479
+ end
480
+ private_class_method :make_product
481
+ end
482
+
483
+ class PromisesPromisedKeyAction
484
+ extend FunctionalLightService::Action
485
+
486
+ promises :promised_key
487
+
488
+ executed do |ctx|
489
+ ctx.promised_key = "promised_key"
490
+ end
491
+ end
492
+
493
+ class ExpectsExpectedKeyAction
494
+ extend FunctionalLightService::Action
495
+
496
+ expects :expected_key
497
+ promises :final_key
498
+
499
+ executed do |ctx|
500
+ ctx.final_key = ctx.expected_key
501
+ end
502
+ end
503
+
504
+ class NullAction
505
+ extend FunctionalLightService::Action
506
+
507
+ # rubocop:disable Lint/EmptyBlock
508
+ executed { |_ctx| }
509
+ # rubocop:enable Lint/EmptyBlock
510
+ end
511
+
512
+ class TestIterate
513
+ extend FunctionalLightService::Organizer
514
+
515
+ def self.call(context)
516
+ with(context)
517
+ .reduce([iterate(:counters,
518
+ [TestDoubles::AddsOneAction])])
519
+ end
520
+
521
+ def self.call_single(context)
522
+ with(context)
523
+ .reduce([iterate(:counters,
524
+ TestDoubles::AddsOneAction)])
525
+ end
526
+ end
527
+
528
+ class TestWithCallback
529
+ extend FunctionalLightService::Organizer
530
+
531
+ def self.call(context = {})
532
+ with(context).reduce(actions)
533
+ end
534
+
535
+ def self.actions
536
+ [
537
+ SetUpContextAction,
538
+ with_callback(IterateCollectionAction,
539
+ [IncrementCountAction,
540
+ AddToTotalAction])
541
+ ]
542
+ end
543
+ end
544
+
545
+ class SetUpContextAction
546
+ extend FunctionalLightService::Action
547
+
548
+ promises :numbers, :counter, :total
549
+
550
+ executed do |ctx|
551
+ ctx.numbers = [1, 2, 3]
552
+ ctx.counter = 0
553
+ ctx.total = 0
554
+ end
555
+ end
556
+
557
+ class IterateCollectionAction
558
+ extend FunctionalLightService::Action
559
+
560
+ expects :numbers, :callback
561
+ promises :number
562
+
563
+ executed do |ctx|
564
+ ctx.numbers.each do |number|
565
+ ctx.number = number
566
+ ctx.callback.call(ctx)
567
+ end
568
+ end
569
+ end
570
+
571
+ class IncrementCountAction
572
+ extend FunctionalLightService::Action
573
+
574
+ expects :counter
575
+
576
+ executed do |ctx|
577
+ ctx.counter = ctx.counter + 1
578
+ end
579
+ end
580
+
581
+ class AddToTotalAction
582
+ extend FunctionalLightService::Action
583
+
584
+ expects :number, :total
585
+
586
+ executed do |ctx|
587
+ ctx.total += ctx.number
588
+ end
589
+ end
590
+
591
+ class CapitalizeMessage
592
+ extend FunctionalLightService::Action
593
+
594
+ expects :a_message
595
+ promises :final_message
596
+
597
+ executed do |ctx|
598
+ ctx.final_message = ctx.a_message.upcase
599
+ end
600
+ end
601
+
602
+ class AnOrganizerThatAddsToContext
603
+ extend FunctionalLightService::Organizer
604
+
605
+ def self.call
606
+ with.reduce(actions)
607
+ end
608
+
609
+ def self.actions
610
+ [add_to_context(
611
+ :strongest_avenger => :thor,
612
+ :last_jedi => "Rey"
613
+ )]
614
+ end
615
+ end
616
+
617
+ class AnOrganizerThatAddsAliases
618
+ extend FunctionalLightService::Organizer
619
+
620
+ def self.call
621
+ with(:foo => :bar).reduce(actions)
622
+ end
623
+
624
+ def self.actions
625
+ [add_aliases(:foo => :baz)]
626
+ end
627
+ end
628
+ end