bcdd-result 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +31 -11
  3. data/CHANGELOG.md +148 -0
  4. data/README.md +849 -242
  5. data/Rakefile +9 -3
  6. data/Steepfile +1 -1
  7. data/lib/bcdd/result/config/constant_alias.rb +33 -0
  8. data/lib/bcdd/result/config/options.rb +26 -0
  9. data/lib/bcdd/result/config/switcher.rb +82 -0
  10. data/lib/bcdd/result/config.rb +71 -0
  11. data/lib/bcdd/result/context/expectations/mixin.rb +23 -0
  12. data/lib/bcdd/result/context/expectations.rb +25 -0
  13. data/lib/bcdd/result/context/failure.rb +9 -0
  14. data/lib/bcdd/result/context/mixin.rb +41 -0
  15. data/lib/bcdd/result/context/success.rb +15 -0
  16. data/lib/bcdd/result/context.rb +74 -0
  17. data/lib/bcdd/result/{expectations/contract → contract}/disabled.rb +2 -2
  18. data/lib/bcdd/result/{expectations → contract}/error.rb +5 -3
  19. data/lib/bcdd/result/{expectations/contract → contract}/evaluator.rb +2 -2
  20. data/lib/bcdd/result/{expectations/contract → contract}/for_types.rb +2 -2
  21. data/lib/bcdd/result/contract/for_types_and_values.rb +44 -0
  22. data/lib/bcdd/result/contract/interface.rb +21 -0
  23. data/lib/bcdd/result/{expectations → contract}/type_checker.rb +1 -1
  24. data/lib/bcdd/result/contract.rb +33 -0
  25. data/lib/bcdd/result/error.rb +7 -9
  26. data/lib/bcdd/result/expectations/mixin.rb +19 -12
  27. data/lib/bcdd/result/expectations.rb +51 -36
  28. data/lib/bcdd/result/failure/methods.rb +21 -0
  29. data/lib/bcdd/result/failure.rb +2 -16
  30. data/lib/bcdd/result/mixin.rb +26 -8
  31. data/lib/bcdd/result/success/methods.rb +21 -0
  32. data/lib/bcdd/result/success.rb +2 -16
  33. data/lib/bcdd/result/version.rb +1 -1
  34. data/lib/bcdd/result.rb +17 -4
  35. data/lib/bcdd-result.rb +3 -0
  36. data/sig/bcdd/result.rbs +340 -88
  37. metadata +27 -16
  38. data/lib/bcdd/result/expectations/contract/for_types_and_values.rb +0 -37
  39. data/lib/bcdd/result/expectations/contract/interface.rb +0 -21
  40. data/lib/bcdd/result/expectations/contract.rb +0 -25
  41. data/lib/result.rb +0 -5
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <h1 align="center" id="-bcddresult">🔀 BCDD::Result</h1>
3
- <p align="center"><i>Empower Ruby apps with a pragmatic use of Railway Oriented Programming.</i></p>
3
+ <p align="center"><i>Empower Ruby apps with pragmatic use of Result monad, Railway Oriented Programming, and B/CDD.</i></p>
4
4
  <p align="center">
5
5
  <img src="https://img.shields.io/badge/ruby->%3D%202.7.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
6
6
  <a href="https://rubygems.org/gems/bcdd-result"><img src="https://badge.fury.io/rb/bcdd-result.svg" alt="bcdd-result gem version" height="18"></a>
@@ -23,7 +23,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
23
23
  - [`BCDD::Result` *versus* `Result`](#bcddresult-versus-result)
24
24
  - [Reference](#reference)
25
25
  - [Result Attributes](#result-attributes)
26
- - [Receiving types in `result.success?` or `result.failure?`](#receiving-types-in-resultsuccess-or-resultfailure)
26
+ - [Checking types with `result.success?` or `result.failure?`](#checking-types-with-resultsuccess-or-resultfailure)
27
27
  - [Result Hooks](#result-hooks)
28
28
  - [`result.on`](#resulton)
29
29
  - [`result.on_type`](#resulton_type)
@@ -35,6 +35,9 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
35
35
  - [`result.value_or`](#resultvalue_or)
36
36
  - [Result Data](#result-data)
37
37
  - [`result.data`](#resultdata)
38
+ - [Pattern Matching](#pattern-matching)
39
+ - [`Array`/`Find` patterns](#arrayfind-patterns)
40
+ - [`Hash` patterns](#hash-patterns)
38
41
  - [Railway Oriented Programming](#railway-oriented-programming)
39
42
  - [`result.and_then`](#resultand_then)
40
43
  - [`BCDD::Result.mixin`](#bcddresultmixin)
@@ -42,12 +45,8 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
42
45
  - [Module example (Singleton Methods)](#module-example-singleton-methods)
43
46
  - [Important Requirement](#important-requirement)
44
47
  - [Dependency Injection](#dependency-injection)
45
- - [Addons](#addons)
46
- - [Pattern Matching](#pattern-matching)
47
- - [`Array`/`Find` patterns](#arrayfind-patterns)
48
- - [`Hash` patterns](#hash-patterns)
49
- - [BCDD::Result::Expectations](#bcddresultexpectations)
50
- - [`BCDD::Result::Expectations`](#bcddresultexpectations-1)
48
+ - [Add-ons](#add-ons)
49
+ - [`BCDD::Result::Expectations`](#bcddresultexpectations)
51
50
  - [Standalone *versus* Mixin mode](#standalone-versus-mixin-mode)
52
51
  - [Type checking - Result Hooks](#type-checking---result-hooks)
53
52
  - [`#success?` and `#failure?`](#success-and-failure)
@@ -60,7 +59,22 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
60
59
  - [Value checking - Result Creation](#value-checking---result-creation)
61
60
  - [Success()](#success)
62
61
  - [Failure()](#failure)
63
- - [`BCDD::Result::Expectations.mixin` Addons](#bcddresultexpectationsmixin-addons)
62
+ - [Pattern Matching Support](#pattern-matching-support)
63
+ - [`BCDD::Result::Expectations.mixin` add-ons](#bcddresultexpectationsmixin-add-ons)
64
+ - [`BCDD::Result::Context`](#bcddresultcontext)
65
+ - [Defining successes and failures](#defining-successes-and-failures)
66
+ - [`BCDD::Result::Context.mixin`](#bcddresultcontextmixin)
67
+ - [Class example (Instance Methods)](#class-example-instance-methods-1)
68
+ - [`and_expose`](#and_expose)
69
+ - [Module example (Singleton Methods)](#module-example-singleton-methods-1)
70
+ - [`BCDD::Result::Context::Expectations`](#bcddresultcontextexpectations)
71
+ - [Mixin add-ons](#mixin-add-ons)
72
+ - [`BCDD::Result.configuration`](#bcddresultconfiguration)
73
+ - [`config.addon.enable!(:continue)`](#configaddonenablecontinue)
74
+ - [`config.constant_alias.enable!('Result')`](#configconstant_aliasenableresult)
75
+ - [`config.pattern_matching.disable!(:nil_as_valid_value_checking)`](#configpattern_matchingdisablenil_as_valid_value_checking)
76
+ - [`config.feature.disable!(:expectations)`](#configfeaturedisableexpectations)
77
+ - [`BCDD::Result.config`](#bcddresultconfig)
64
78
  - [About](#about)
65
79
  - [Development](#development)
66
80
  - [Contributing](#contributing)
@@ -76,7 +90,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
76
90
  Add this line to your application's Gemfile:
77
91
 
78
92
  ```ruby
79
- gem 'bcdd-result', require: 'bcdd/result'
93
+ gem 'bcdd-result'
80
94
  ```
81
95
 
82
96
  And then execute:
@@ -87,6 +101,10 @@ If bundler is not being used to manage dependencies, install the gem by executin
87
101
 
88
102
  $ gem install bcdd-result
89
103
 
104
+ And require it in your code:
105
+
106
+ require 'bcdd/result'
107
+
90
108
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
91
109
 
92
110
  ## Usage
@@ -111,17 +129,27 @@ BCDD::Result::Failure(:err) #
111
129
 
112
130
  #### `BCDD::Result` *versus* `Result`
113
131
 
114
- The `BCDD::Result` is the main module of this gem. It contains all the features, constants, and methods you will use to create and manipulate results.
132
+ This gem provides a way to create constant aliases for `BCDD::Result` and other classes/modules.
115
133
 
116
- The `Result` is an alias of `BCDD::Result`. It was created to facilitate the use of this gem in the code. So, instead of requiring `BCDD::Result` everywhere, you can require `Result` and use it as an alias.
134
+ To enable it, you must call the `BCDD::Result.configuration` method and pass a block to it. You can turn the aliases you want on/off in this block.
117
135
 
118
136
  ```ruby
119
- require 'result'
137
+ BCDD::Result.configuration do |config|
138
+ config.constant_alias.enable!('Result')
139
+ end
140
+ ```
120
141
 
142
+ So, instead of using `BCDD::Result` everywhere, you can use `Result` as an alias/shortcut.
143
+
144
+ ```ruby
121
145
  Result::Success(:ok) # <BCDD::Result::Success type=:ok value=nil>
146
+
147
+ Result::Failure(:err) # <BCDD::Result::Failure type=:err value=nil>
122
148
  ```
123
149
 
124
- All the examples in this README that use `BCDD::Result` can also be used with `Result`.
150
+ If you have enabled constant aliasing, all examples in this README that use `BCDD::Result` can be implemented using `Result`.
151
+
152
+ There are other aliases and configurations available. Check the [BCDD::Result.configuration]() section for more information.
125
153
 
126
154
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
127
155
 
@@ -161,12 +189,12 @@ result.value # nil
161
189
  ################
162
190
  # With a value #
163
191
  ################
164
- result = BCDD::Result::Failure(:err, my: 'value')
192
+ result = BCDD::Result::Failure(:err, 'my_value')
165
193
 
166
194
  result.success? # false
167
195
  result.failure? # true
168
196
  result.type # :err
169
- result.value # {:my => "value"}
197
+ result.value # "my_value"
170
198
 
171
199
  ###################
172
200
  # Without a value #
@@ -179,9 +207,11 @@ result.type # :no
179
207
  result.value # nil
180
208
  ```
181
209
 
210
+ In both cases, the `type` must be a symbol, and the `value` can be any kind of object.
211
+
182
212
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
183
213
 
184
- #### Receiving types in `result.success?` or `result.failure?`
214
+ #### Checking types with `result.success?` or `result.failure?`
185
215
 
186
216
  `BCDD::Result#success?` and `BCDD::Result#failure?` are methods that allow you to check if the result is a success or a failure.
187
217
 
@@ -190,9 +220,11 @@ You can also check the result type by passing an argument to it. For example, `r
190
220
  ```ruby
191
221
  result = BCDD::Result::Success(:ok)
192
222
 
193
- result.success? # true
194
- result.success?(:ok) # true
195
- result.success?(:okay) # false
223
+ result.success?(:ok)
224
+
225
+ # This is the same as:
226
+
227
+ result.success? && result.type == :ok
196
228
  ```
197
229
 
198
230
  The same is valid for `BCDD::Result#failure?`.
@@ -200,18 +232,19 @@ The same is valid for `BCDD::Result#failure?`.
200
232
  ```ruby
201
233
  result = BCDD::Result::Failure(:err)
202
234
 
203
- result.failure? # true
204
- result.failure?(:err) # true
205
- result.failure?(:error) # false
235
+ result.failure?(:err)
236
+
237
+ # This is the same as:
238
+
239
+ result.failure? && result.type == :err
206
240
  ```
207
241
 
208
242
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
209
243
 
210
244
  ### Result Hooks
211
245
 
212
- Result hooks are methods that allow you to perform a block of code depending on the result type.
213
-
214
- To exemplify the them, I will implement a method that knows how to divide two numbers.
246
+ Result hooks are methods that allow you to execute a block of code based on the type of result obtained.
247
+ To demonstrate their use, I will implement a method that can divide two numbers.
215
248
 
216
249
  ```ruby
217
250
  def divide(arg1, arg2)
@@ -228,14 +261,15 @@ end
228
261
 
229
262
  #### `result.on`
230
263
 
231
- `BCDD::Result#on` will perform the block when the type matches the result type.
264
+ When you use `BCDD::Result#on`, the block will be executed only when the type matches the result type.
232
265
 
233
- Regardless of the block being executed, the return of the method will always be the result itself.
266
+ However, even if the block is executed, the method will always return the result itself.
234
267
 
235
- The result value will be exposed as the first argument of the block.
268
+ The value of the result will be available as the first argument of the block.
236
269
 
237
270
  ```ruby
238
271
  result = divide(nil, 2)
272
+ #<BCDD::Result::Failure type=:invalid_arg data='arg1 must be numeric'>
239
273
 
240
274
  output =
241
275
  result
@@ -268,24 +302,44 @@ result.object_id == output.object_id # true
268
302
 
269
303
  `BCDD::Result#on_type` is an alias of `BCDD::Result#on`.
270
304
 
305
+ ```ruby
306
+ result = divide(nil, 2)
307
+ #<BCDD::Result::Failure type=:invalid_arg data='arg1 must be numeric'>
308
+
309
+ output =
310
+ result
311
+ .on_type(:invalid_arg, :division_by_zero) { |msg| puts msg }
312
+ .on_type(:division_completed) { |number| puts number }
313
+
314
+ # The code above will print 'arg1 must be numeric' and return the result itself.
315
+
316
+ result.object_id == output.object_id # true
317
+ ```
318
+
319
+ *PS: The `divide()` implementation is [here](#result-hooks).*
320
+
321
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
322
+
271
323
  #### `result.on_success`
272
324
 
273
- `BCDD::Result#on_success` is very similar to the `BCDD::Result#on` hook. The main differences are:
325
+ The `BCDD::Result#on_success` method is quite similar to the `BCDD::Result#on` hook, but with a few key differences:
274
326
 
275
- 1. Only perform the block when the result is a success.
276
- 2. If the type is missing it will perform the block for any success.
327
+ 1. It will only execute the block of code if the result is a success.
328
+ 2. If the type declaration is not included, the method will execute the block for any successful result, regardless of its type.
277
329
 
278
330
  ```ruby
279
- # It performs the block and return itself.
331
+ # In both examples, it executes the block and returns the result itself.
280
332
 
281
333
  divide(4, 2).on_success { |number| puts number }
282
334
 
283
335
  divide(4, 2).on_success(:division_completed) { |number| puts number }
284
336
 
285
- # It doesn't perform the block, but return itself.
337
+ # It doesn't execute the block as the type is different.
286
338
 
287
339
  divide(4, 4).on_success(:ok) { |value| puts value }
288
340
 
341
+ # It doesn't execute the block, as the result is a success, but the hook expects a failure.
342
+
289
343
  divide(4, 4).on_failure { |error| puts error }
290
344
  ```
291
345
 
@@ -295,20 +349,25 @@ divide(4, 4).on_failure { |error| puts error }
295
349
 
296
350
  #### `result.on_failure`
297
351
 
298
- Is the opposite of `Result#on_success`.
352
+ It is the opposite of `Result#on_success`:
353
+
354
+ 1. It will only execute the block of code if the result is a failure.
355
+ 2. If the type declaration is not included, the method will execute the block for any failed result, regardless of its type.
299
356
 
300
357
  ```ruby
301
- # It performs the block and return itself.
358
+ # In both examples, it executes the block and returns the result itself.
302
359
 
303
360
  divide(nil, 2).on_failure { |error| puts error }
304
361
 
305
- divide(4, 0).on_failure(:invalid_arg, :division_by_zero) { |error| puts error }
362
+ divide(4, 0).on_failure(:division_by_zero) { |error| puts error }
306
363
 
307
- # It doesn't perform the block, but return itself.
308
-
309
- divide(4, 0).on_success { |number| puts number }
364
+ # It doesn't execute the block as the type is different.
310
365
 
311
366
  divide(4, 0).on_failure(:invalid_arg) { |error| puts error }
367
+
368
+ # It doesn't execute the block, as the result is a failure, but the hook expects a success.
369
+
370
+ divide(4, 0).on_success { |number| puts number }
312
371
  ```
313
372
 
314
373
  *PS: The `divide()` implementation is [here](#result-hooks).*
@@ -317,11 +376,11 @@ divide(4, 0).on_failure(:invalid_arg) { |error| puts error }
317
376
 
318
377
  #### `result.on_unknown`
319
378
 
320
- `BCDD::Result#on_unknown` will perform the block when no other hook (`#on`, `#on_type`, `#on_failure`, `#on_success`) has been executed.
379
+ `BCDD::Result#on_unknown` will execute the block when no other hook (`#on`, `#on_type`, `#on_failure`, `#on_success`) has been executed.
321
380
 
322
- Regardless of the block being executed, the return of the method will always be the result itself.
381
+ Regardless of the block being executed, the method will always return the result itself.
323
382
 
324
- The result value will be exposed as the first argument of the block.
383
+ The value of the result will be available as the first argument of the block.
325
384
 
326
385
  ```ruby
327
386
  divide(4, 2)
@@ -338,7 +397,7 @@ divide(4, 2)
338
397
 
339
398
  #### `result.handle`
340
399
 
341
- This method will allow you to define blocks for each hook (type, failure, success), but instead of returning itself, it will return the output of the first match/block execution.
400
+ This method lets you define blocks for each hook (type, failure, or success), but instead of returning itself, it will return the output of the first match/block execution.
342
401
 
343
402
  ```ruby
344
403
  divide(4, 2).handle do |result|
@@ -379,8 +438,8 @@ end
379
438
 
380
439
  **Notes:**
381
440
  * You can define multiple types to be handled by the same hook/block
382
- * If the type is missing, it will perform the block for any success or failure handler.
383
- * The `#type` and `#[]` handlers will require at least one type/argument.
441
+ * If the type is missing, it will execute the block for any success or failure handler.
442
+ * The `#type` and `#[]` handlers require at least one type/argument.
384
443
 
385
444
  *PS: The `divide()` implementation is [here](#result-hooks).*
386
445
 
@@ -388,13 +447,13 @@ end
388
447
 
389
448
  ### Result Value
390
449
 
391
- The most simple way to get the result value is by calling `BCDD::Result#value`.
450
+ To access the result value, you can simply call `BCDD::Result#value`.
392
451
 
393
- But sometimes you need to get the value of a successful result or a default value if it is a failure. In this case, you can use `BCDD::Result#value_or`.
452
+ However, there may be instances where you need to retrieve the value of a successful result or a default value if the result is a failure. In such cases, you can make use of `BCDD::Result#value_or`.
394
453
 
395
454
  #### `result.value_or`
396
455
 
397
- `BCCD::Result#value_or` returns the value when the result is a success, but if is a failure the block will be performed, and its outcome will be the output.
456
+ `BCCD::Result#value_or` returns the value when the result is a success. However, if it is a failure, the given block will be executed, and its outcome will be returned.
398
457
 
399
458
  ```ruby
400
459
  def divide(arg1, arg2)
@@ -462,16 +521,75 @@ print_to_hash(**success_data) # [:success, :ok, 1]
462
521
 
463
522
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
464
523
 
465
- ### Railway Oriented Programming
524
+ ### Pattern Matching
525
+
526
+ The `BCDD::Result` also provides support to pattern matching.
527
+
528
+ In the further examples, I will use the `Divide` lambda to exemplify its usage.
529
+
530
+ ```ruby
531
+ Divide = lambda do |arg1, arg2|
532
+ arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric')
533
+ arg2.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg2 must be numeric')
534
+
535
+ return BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?
536
+
537
+ BCDD::Result::Success(:division_completed, arg1 / arg2)
538
+ end
539
+ ```
540
+
541
+ #### `Array`/`Find` patterns
466
542
 
467
- This feature/pattern is also known as ["Railway Oriented Programming"](https://fsharpforfunandprofit.com/rop/).
543
+ ```ruby
544
+ case Divide.call(4, 2)
545
+ in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
546
+ in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
547
+ in BCDD::Result::Success[:division_completed, value] then puts value
548
+ end
549
+
550
+ # The code above will print: 2
551
+
552
+ case Divide.call(4, 0)
553
+ in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
554
+ in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
555
+ in BCDD::Result::Success[:division_completed, value] then puts value
556
+ end
557
+
558
+ # The code above will print: arg2 must not be zero
559
+ ```
560
+
561
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
562
+
563
+ #### `Hash` patterns
564
+
565
+ ```ruby
566
+ case Divide.call(10, 2)
567
+ in { failure: { invalid_arg: msg } } then puts msg
568
+ in { failure: { division_by_zero: msg } } then puts msg
569
+ in { success: { division_completed: value } } then puts value
570
+ end
571
+
572
+ # The code above will print: 5
573
+
574
+ case Divide.call('10', 2)
575
+ in { failure: { invalid_arg: msg } } then puts msg
576
+ in { failure: { division_by_zero: msg } } then puts msg
577
+ in { success: { division_completed: value } } then puts value
578
+ end
579
+
580
+ # The code above will print: arg1 must be numeric
581
+ ```
582
+
583
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
584
+
585
+ ### Railway Oriented Programming
468
586
 
469
- The idea is to chain blocks and creates a pipeline of operations that can be interrupted by a failure.
587
+ ["Railway Oriented Programming (ROP)"](https://fsharpforfunandprofit.com/rop/) is a programming technique that involves linking blocks together to form a sequence of operations, also known as a pipeline.
588
+ If a failure occurs in any of the blocks, the pipeline is interrupted and subsequent blocks are skipped.
470
589
 
471
- In other words, the block will be executed only if the result is a success.
472
- So, if some block returns a failure, the following blocks will be skipped.
590
+ The ROP technique allows you to structure your code in a way that expresses your logic as a series of operations, with the added benefit of stopping the process at the first detection of failure.
473
591
 
474
- Due to this characteristic, you can use this feature to express some logic as a sequence of operations. And have the guarantee that the process will stop by the first failure detection, and if everything is ok, the final result will be a success.
592
+ If all blocks successfully execute, the final result of the pipeline will be a success.
475
593
 
476
594
  #### `result.and_then`
477
595
 
@@ -481,7 +599,7 @@ module Divide
481
599
 
482
600
  def call(arg1, arg2)
483
601
  validate_numbers(arg1, arg2)
484
- .and_then { |numbers| validate_non_zero(numbers) }
602
+ .and_then { |numbers| validate_nonzero(numbers) }
485
603
  .and_then { |numbers| divide(numbers) }
486
604
  end
487
605
 
@@ -494,8 +612,8 @@ module Divide
494
612
  BCDD::Result::Success(:ok, [arg1, arg2])
495
613
  end
496
614
 
497
- def validate_non_zero(numbers)
498
- return BCDD::Result::Success(:ok, numbers) unless numbers.last.zero?
615
+ def validate_nonzero(numbers)
616
+ return BCDD::Result::Success(:ok, numbers) if numbers.last.nonzero?
499
617
 
500
618
  BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero')
501
619
  end
@@ -526,9 +644,11 @@ Divide.call(2, 2)
526
644
 
527
645
  #### `BCDD::Result.mixin`
528
646
 
529
- This method produces a module that can be included/extended by any object. It adds two methods to the target object: `Success()` and `Failure()`. The main difference between these methods and `BCDD::Result::Success()`/`BCDD::Result::Failure()` is that the first ones will use the target object (who received the include/extend) as the result's subject.
647
+ This method generates a module that any object can include or extend. It adds two methods to the target object: `Success()` and `Failure()`.
648
+
649
+ The main difference between these methods and `BCDD::Result::Success()`/`BCDD::Result::Failure()` is that the former will utilize the target object (which has received the include/extend) as the result's subject.
530
650
 
531
- And because of this, you can use the `#and_then` method to call methods from the result's subject.
651
+ Because the result has a subject, the `#and_then` method can call methods from it.
532
652
 
533
653
  ##### Class example (Instance Methods)
534
654
 
@@ -545,7 +665,7 @@ class Divide
545
665
 
546
666
  def call
547
667
  validate_numbers
548
- .and_then(:validate_non_zero)
668
+ .and_then(:validate_nonzero)
549
669
  .and_then(:divide)
550
670
  end
551
671
 
@@ -560,7 +680,7 @@ class Divide
560
680
  Success(:ok, [arg1, arg2])
561
681
  end
562
682
 
563
- def validate_non_zero(numbers)
683
+ def validate_nonzero(numbers)
564
684
  return Success(:ok, numbers) unless numbers.last.zero?
565
685
 
566
686
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -586,7 +706,7 @@ module Divide
586
706
 
587
707
  def call(arg1, arg2)
588
708
  validate_numbers(arg1, arg2)
589
- .and_then(:validate_non_zero)
709
+ .and_then(:validate_nonzero)
590
710
  .and_then(:divide)
591
711
  end
592
712
 
@@ -599,7 +719,7 @@ module Divide
599
719
  Success(:ok, [arg1, arg2])
600
720
  end
601
721
 
602
- def validate_non_zero(numbers)
722
+ def validate_nonzero(numbers)
603
723
  return Success(:ok, numbers) unless numbers.last.zero?
604
724
 
605
725
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -621,17 +741,122 @@ Divide.call(4, '2') #<BCDD::Result::Failure type=:invalid_arg value="arg2 must b
621
741
 
622
742
  ##### Important Requirement
623
743
 
624
- The unique condition for using the `#and_then` to call methods is that they must use the `Success()` and `Failure()` to produce their results.
744
+ To use the `#and_then` method to call methods, they must use `Success()` and `Failure()` to produce the results.
745
+
746
+ If you try to use `BCDD::Result::Subject()`/`BCDD::Result::Failure()`, or results from another `BCDD::Result.mixin` instance with `#and_then`, it will raise an error because the subjects will be different.
747
+
748
+ **Note:** You can still use the block syntax, but all the results must be produced by the subject's `Success()` and `Failure()` methods.
749
+
750
+ ```ruby
751
+ module ValidateNonzero
752
+ extend self, BCDD::Result.mixin
753
+
754
+ def call(numbers)
755
+ return Success(:ok, numbers) unless numbers.last.zero?
756
+
757
+ Failure(:division_by_zero, 'arg2 must not be zero')
758
+ end
759
+ end
760
+
761
+ module Divide
762
+ extend self, BCDD::Result.mixin
763
+
764
+ def call(arg1, arg2)
765
+ validate_numbers(arg1, arg2)
766
+ .and_then(:validate_nonzero)
767
+ .and_then(:divide)
768
+ end
769
+
770
+ private
771
+
772
+ def validate_numbers(arg1, arg2)
773
+ arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
774
+ arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
775
+
776
+ Success(:ok, [arg1, arg2])
777
+ end
778
+
779
+ def validate_nonzero(numbers)
780
+ ValidateNonzero.call(numbers) # This will raise an error
781
+ end
782
+
783
+ def divide((number1, number2))
784
+ Success(:division_completed, number1 / number2)
785
+ end
786
+ end
787
+ ```
788
+
789
+ Look at the error produced by the code above:
790
+
791
+ ```ruby
792
+ Divide.call(2, 0)
793
+
794
+ # You cannot call #and_then and return a result that does not belong to the subject! (BCDD::Result::Error::InvalidResultSubject)
795
+ # Expected subject: Divide
796
+ # Given subject: ValidateNonzero
797
+ # Given result: #<BCDD::Result::Failure type=:division_by_zero value="arg2 must not be zero">
798
+ ```
799
+
800
+ In order to fix this, you must handle the result produced by `ValidateNonzero.call()` and return a result that belongs to the subject.
801
+
802
+ ```ruby
803
+ module ValidateNonzero
804
+ extend self, BCDD::Result.mixin
805
+
806
+ def call(numbers)
807
+ return Success(:ok, numbers) unless numbers.last.zero?
808
+
809
+ Failure(:division_by_zero, 'arg2 must not be zero')
810
+ end
811
+ end
625
812
 
626
- If you use `BCDD::Result::Subject()`/`BCDD::Result::Failure()`, or use result from another `BCDD::Result::Mixin` instance, the `#and_then` will raise an error because the subjects will be different.
813
+ module Divide
814
+ extend self, BCDD::Result.mixin
815
+
816
+ def call(arg1, arg2)
817
+ validate_numbers(arg1, arg2)
818
+ .and_then(:validate_nonzero)
819
+ .and_then(:divide)
820
+ end
821
+
822
+ private
823
+
824
+ def validate_numbers(arg1, arg2)
825
+ arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
826
+ arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
827
+
828
+ Success(:ok, [arg1, arg2])
829
+ end
627
830
 
628
- > **Note**: You still can use the block syntax, but all the results must be produced by the subject's `Success()` and `Failure()` methods.
831
+ def validate_nonzero(numbers)
832
+ # In this case we are handling the other subject result and returning our own
833
+ ValidateNonzero.call(numbers).handle do |on|
834
+ on.success { |numbers| Success(:ok, numbers) }
835
+
836
+ on.failure { |err| Failure(:division_by_zero, err) }
837
+ end
838
+ end
839
+
840
+ def divide((number1, number2))
841
+ Success(:division_completed, number1 / number2)
842
+ end
843
+ end
844
+ ```
845
+
846
+ Look at the output of the code above:
847
+
848
+ ```ruby
849
+ Divide.call(2, 0)
850
+
851
+ #<BCDD::Result::Failure type=:division_by_zero value="arg2 must not be zero">
852
+ ```
629
853
 
630
854
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
631
855
 
632
856
  ##### Dependency Injection
633
857
 
634
- The `BCDD::Result#and_then` accepts a second argument that will be used to share a value with the subject's method. To receive this argument, the subject's method must have an arity of two, where the first argument will be the result value and the second will be the shared value.
858
+ The `BCDD::Result#and_then` accepts a second argument that will be used to share a value with the subject's method.
859
+ To receive this argument, the subject's method must have an arity of two, where the first argument will be the result value and the second will be the shared value.
635
860
 
636
861
  ```ruby
637
862
  require 'logger'
@@ -641,7 +866,7 @@ module Divide
641
866
 
642
867
  def call(arg1, arg2, logger: ::Logger.new(STDOUT))
643
868
  validate_numbers(arg1, arg2)
644
- .and_then(:validate_non_zero, logger)
869
+ .and_then(:validate_nonzero, logger)
645
870
  .and_then(:divide, logger)
646
871
  end
647
872
 
@@ -654,7 +879,7 @@ module Divide
654
879
  Success(:ok, [arg1, arg2])
655
880
  end
656
881
 
657
- def validate_non_zero(numbers, logger)
882
+ def validate_nonzero(numbers, logger)
658
883
  if numbers.last.zero?
659
884
  logger.error('arg2 must not be zero')
660
885
 
@@ -686,21 +911,21 @@ Divide.call(4, 2, logger: Logger.new(IO::NULL))
686
911
 
687
912
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
688
913
 
689
- ##### Addons
914
+ ##### Add-ons
690
915
 
691
- The `BCDD::Result.mixin` also accepts the `with:` argument. It is a hash that will be used to define the methods that will be added to the target object.
916
+ The `BCDD::Result.mixin` also accepts the `config:` argument. It is a hash that will be used to define custom behaviors for the mixin.
692
917
 
693
- **Continue**
918
+ **continue**
694
919
 
695
920
  This addon will create the `Continue(value)` method, which will know how to produce a `Success(:continued, value)`. It is useful when you want to perform a sequence of operations but want to avoid returning a specific result for each step.
696
921
 
697
922
  ```ruby
698
923
  module Divide
699
- extend self, BCDD::Result.mixin(with: :Continue)
924
+ extend self, BCDD::Result.mixin(config: { addon: { continue: true } })
700
925
 
701
926
  def call(arg1, arg2)
702
927
  validate_numbers(arg1, arg2)
703
- .and_then(:validate_non_zero)
928
+ .and_then(:validate_nonzero)
704
929
  .and_then(:divide)
705
930
  end
706
931
 
@@ -713,7 +938,7 @@ module Divide
713
938
  Continue([arg1, arg2])
714
939
  end
715
940
 
716
- def validate_non_zero(numbers)
941
+ def validate_nonzero(numbers)
717
942
  return Continue(numbers) unless numbers.last.zero?
718
943
 
719
944
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -727,139 +952,49 @@ end
727
952
 
728
953
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
729
954
 
730
- ### Pattern Matching
731
-
732
- The `BCDD::Result` also provides support to pattern matching.
733
-
734
- In the further examples, I will use the `Divide` lambda to exemplify its usage.
955
+ ### `BCDD::Result::Expectations`
735
956
 
736
- ```ruby
737
- Divide = lambda do |arg1, arg2|
738
- arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric')
739
- arg2.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg2 must be numeric')
957
+ This feature lets you define contracts for your results' types and values. There are two ways to use it: the standalone (`BCDD::Result::Expectations.new`) and the mixin (`BCDD::Result::Expectations.mixin`) mode.
740
958
 
741
- return BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?
959
+ It was designed to ensure all the aspects of the result's type and value. So, an error will be raised if you try to create or handle a result with an unexpected type or value.
742
960
 
743
- BCDD::Result::Success(:division_completed, arg1 / arg2)
744
- end
745
- ```
961
+ #### Standalone *versus* Mixin mode
746
962
 
747
- #### `Array`/`Find` patterns
963
+ The _**standalone mode**_ creates an object that knows how to produce and validate results based on the defined expectations. Look at the example below:
748
964
 
749
965
  ```ruby
750
- case Divide.call(4, 2)
751
- in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
752
- in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
753
- in BCDD::Result::Success[:division_completed, value] then puts value
754
- end
966
+ module Divide
967
+ Result = BCDD::Result::Expectations.new(
968
+ success: %i[numbers division_completed],
969
+ failure: %i[invalid_arg division_by_zero]
970
+ )
755
971
 
756
- # The code above will print: 2
972
+ def self.call(arg1, arg2)
973
+ arg1.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg1 must be numeric')
974
+ arg2.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg2 must be numeric')
757
975
 
758
- case Divide.call(4, 0)
759
- in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
760
- in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
761
- in BCDD::Result::Success[:division_completed, value] then puts value
762
- end
976
+ arg2.zero? and return Result::Failure(:division_by_zero, 'arg2 must not be zero')
763
977
 
764
- # The code above will print: arg2 must not be zero
978
+ Result::Success(:division_completed, arg1 / arg2)
979
+ end
980
+ end
765
981
  ```
766
982
 
767
- <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
983
+ In the code above, we define a constant `Divide::Expected`. And because of this (it is a constant), we can use it inside and outside the module.
768
984
 
769
- #### `Hash` patterns
985
+ Look what happens if you try to create a result without one of the expected types.
770
986
 
771
987
  ```ruby
772
- case Divide.call(10, 2)
773
- in { failure: { invalid_arg: msg } } then puts msg
774
- in { failure: { division_by_zero: msg } } then puts msg
775
- in { success: { division_completed: value } } then puts value
776
- end
777
-
778
- # The code above will print: 5
779
-
780
- case Divide.call('10', 2)
781
- in { failure: { invalid_arg: msg } } then puts msg
782
- in { failure: { division_by_zero: msg } } then puts msg
783
- in { success: { division_completed: value } } then puts value
784
- end
988
+ Divide::Result::Success(:ok)
989
+ # type :ok is not allowed. Allowed types: :numbers, :division_completed
990
+ # (BCDD::Result::Contract::Error::UnexpectedType)
785
991
 
786
- # The code above will print: arg1 must be numeric
992
+ Divide::Result::Failure(:err)
993
+ # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
994
+ # (BCDD::Result::Contract::Error::UnexpectedType)
787
995
  ```
788
996
 
789
- <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
790
-
791
- #### BCDD::Result::Expectations
792
-
793
- I'd like you to please read the following section to understand how to use this feature.
794
-
795
- But if you are using Ruby >= 3.0, you can use the `in` operator to use the pattern matching to validate the result's value.
796
-
797
- ```ruby
798
- module Divide
799
- extend BCDD::Result::Expectations.mixin(
800
- success: {
801
- numbers: ->(value) { value in [Numeric, Numeric] },
802
- division_completed: ->(value) { value in (Integer | Float) }
803
- },
804
- failure: { invalid_arg: String, division_by_zero: String }
805
- )
806
-
807
- def self.call(arg1, arg2)
808
- arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
809
- arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
810
-
811
- arg2.zero? and return Failure(:division_by_zero, 'arg2 must not be zero')
812
-
813
- Success(:division_completed, arg1 / arg2)
814
- end
815
- end
816
- ```
817
-
818
- <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
819
-
820
- ### `BCDD::Result::Expectations`
821
-
822
- This feature lets you define contracts for your results' types and values. There are two ways to use it: the standalone (`BCDD::Result::Expectations.new`) and the mixin (`BCDD::Result::Expectations.mixin`) mode.
823
-
824
- It was designed to ensure all the aspects of the result's type and value. So, an error will be raised if you try to create or handle a result with an unexpected type or value.
825
-
826
- #### Standalone *versus* Mixin mode
827
-
828
- The _**standalone mode**_ creates an object that knows how to produce and validate results based on the defined expectations. Look at the example below:
829
-
830
- ```ruby
831
- module Divide
832
- Expected = BCDD::Result::Expectations.new(
833
- success: %i[numbers division_completed],
834
- failure: %i[invalid_arg division_by_zero]
835
- )
836
-
837
- def self.call(arg1, arg2)
838
- arg1.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg1 must be numeric')
839
- arg2.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg2 must be numeric')
840
-
841
- arg2.zero? and return Expected::Failure(:division_by_zero, 'arg2 must not be zero')
842
-
843
- Expected::Success(:division_completed, arg1 / arg2)
844
- end
845
- end
846
- ```
847
-
848
- In the code above, we define a constant `Divide::Expected`. And because of this (it is a constant), we can use it inside and outside the module.
849
-
850
- Look what happens if you try to create a result without one of the expected types.
851
-
852
- ```ruby
853
- Divide::Expected::Success(:ok)
854
- # type :ok is not allowed. Allowed types: :numbers, :division_completed
855
- # (BCDD::Result::Expectations::Error::UnexpectedType)
856
-
857
- Divide::Expected::Failure(:err)
858
- # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
859
- # (BCDD::Result::Expectations::Error::UnexpectedType)
860
- ```
861
-
862
- The _**mixin mode**_ is similar to `BCDD::Result::Mixin`, but it also defines the expectations for the result's types and values.
997
+ The _**mixin mode**_ is similar to `BCDD::Result::Mixin`, but it also defines the expectations for the result's types and values.
863
998
 
864
999
  ```ruby
865
1000
  class Divide
@@ -870,7 +1005,7 @@ class Divide
870
1005
 
871
1006
  def call(arg1, arg2)
872
1007
  validate_numbers(arg1, arg2)
873
- .and_then(:validate_non_zero)
1008
+ .and_then(:validate_nonzero)
874
1009
  .and_then(:divide)
875
1010
  end
876
1011
 
@@ -883,7 +1018,7 @@ class Divide
883
1018
  Success(:numbers, [arg1, arg2])
884
1019
  end
885
1020
 
886
- def validate_non_zero(numbers)
1021
+ def validate_nonzero(numbers)
887
1022
  return Success(:numbers, numbers) unless numbers.last.zero?
888
1023
 
889
1024
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -895,10 +1030,10 @@ class Divide
895
1030
  end
896
1031
  ```
897
1032
 
898
- This mode also defines an `Expected` constant to be used inside and outside the module.
1033
+ This mode also defines an `Result` constant to be used inside and outside the module.
899
1034
 
900
1035
  > **PROTIP:**
901
- > You can use the `Expected` constant to mock the result's type and value in your tests. As they will have the exact expectations, your tests will check if the result clients are handling the result correctly.
1036
+ > You can use the `Result` constant to mock the result's type and value in your tests. As they will have the exact expectations, your tests will check if the result clients are handling the result correctly.
902
1037
 
903
1038
  Now that you know the two modes, let's understand how expectations can be beneficial and powerful for defining contracts.
904
1039
 
@@ -906,11 +1041,11 @@ Now that you know the two modes, let's understand how expectations can be benefi
906
1041
 
907
1042
  #### Type checking - Result Hooks
908
1043
 
909
- The `BCDD::Result::Expectations` will check if the result's type is valid. This checking will be performed in all the methods that rely on the result's type, like `#success?`, `#failure?`, `#on`, `#on_type`, `#on_success`, `#on_failure`, `#handle`.
1044
+ The `BCDD::Result::Expectations` will check if the type of the result is valid. This checking will be performed in all methods that depend on the results type, such as `#success?`, `#failure?`, `#on`, `#on_type`, `#on_success`, `#on_failure`, and `#handle`.
910
1045
 
911
1046
  ##### `#success?` and `#failure?`
912
1047
 
913
- When you check whether a result is a success or failure, `BCDD::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.
1048
+ When checking whether a result is a success or failure, `BCDD::Result::Expectations` will also verify if the result type is valid/expected. In case of an invalid type, an error will be raised.
914
1049
 
915
1050
  **Success example:**
916
1051
 
@@ -923,7 +1058,7 @@ result.success?(:division_completed) # true
923
1058
 
924
1059
  result.success?(:ok)
925
1060
  # type :ok is not allowed. Allowed types: :numbers, :division_completed
926
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1061
+ # (BCDD::Result::Contract::Error::UnexpectedType)
927
1062
  ```
928
1063
 
929
1064
  **Failure example:**
@@ -937,7 +1072,7 @@ result.failure?(:division_by_zero) # false
937
1072
 
938
1073
  result.failure?(:err)
939
1074
  # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
940
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1075
+ # (BCDD::Result::Contract::Error::UnexpectedType)
941
1076
  ```
942
1077
 
943
1078
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -946,7 +1081,7 @@ result.failure?(:err)
946
1081
 
947
1082
  ##### `#on` and `#on_type`
948
1083
 
949
- If you use `#on` or `#on_type` to perform a block, `BCDD::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.
1084
+ If you use `#on` or `#on_type` to execute a block, `BCDD::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.
950
1085
 
951
1086
  ```ruby
952
1087
  result = Divide.new.call(10, 2)
@@ -959,7 +1094,7 @@ result
959
1094
 
960
1095
  result.on(:number) { |_| :this_type_does_not_exist }
961
1096
  # type :number is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero
962
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1097
+ # (BCDD::Result::Contract::Error::UnexpectedType)
963
1098
  ```
964
1099
 
965
1100
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -968,7 +1103,7 @@ result.on(:number) { |_| :this_type_does_not_exist }
968
1103
 
969
1104
  ##### `#on_success` and `#on_failure`
970
1105
 
971
- If you use `#on_success` or `#on_failure` to perform a block, `BCDD::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.
1106
+ If you use `#on_success` or `#on_failure` to execute a block, `BCDD::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.
972
1107
 
973
1108
  ```ruby
974
1109
  result = Divide.new.call(10, '2')
@@ -985,11 +1120,11 @@ result
985
1120
 
986
1121
  result.on_success(:ok) { |_| :this_type_does_not_exist }
987
1122
  # type :ok is not allowed. Allowed types: :numbers, :division_completed
988
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1123
+ # (BCDD::Result::Contract::Error::UnexpectedType)
989
1124
 
990
1125
  result.on_failure(:err) { |_| :this_type_does_not_exist }
991
1126
  # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
992
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1127
+ # (BCDD::Result::Contract::Error::UnexpectedType)
993
1128
  ```
994
1129
 
995
1130
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -1006,17 +1141,17 @@ result = Divide.call(10, 2)
1006
1141
  result.handle do |on|
1007
1142
  on.type(:ok) { |_| :this_type_does_not_exist }
1008
1143
  end
1009
- # type :ok is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero (BCDD::Result::Expectations::Error::UnexpectedType)
1144
+ # type :ok is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
1010
1145
 
1011
1146
  result.handle do |on|
1012
1147
  on.success(:ok) { |_| :this_type_does_not_exist }
1013
1148
  end
1014
- # type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Expectations::Error::UnexpectedType)
1149
+ # type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
1015
1150
 
1016
1151
  result.handle do |on|
1017
1152
  on.failure(:err) { |_| :this_type_does_not_exist }
1018
1153
  end
1019
- # type :err is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Expectations::Error::UnexpectedType)
1154
+ # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
1020
1155
  ```
1021
1156
 
1022
1157
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -1047,11 +1182,11 @@ end
1047
1182
 
1048
1183
  Divide.call('4', 2)
1049
1184
  # type :invalid_arg is not allowed. Allowed types: :err
1050
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1185
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1051
1186
 
1052
1187
  Divide.call(4, 2)
1053
1188
  # type :division_completed is not allowed. Allowed types: :ok
1054
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1189
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1055
1190
  ```
1056
1191
 
1057
1192
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -1060,25 +1195,25 @@ Divide.call(4, 2)
1060
1195
 
1061
1196
  ```ruby
1062
1197
  module Divide
1063
- Expected = BCDD::Result::Expectations.new(success: :ok, failure: :err)
1198
+ Result = BCDD::Result::Expectations.new(success: :ok, failure: :err)
1064
1199
 
1065
1200
  def self.call(arg1, arg2)
1066
- arg1.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg1 must be numeric')
1067
- arg2.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg2 must be numeric')
1201
+ arg1.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg1 must be numeric')
1202
+ arg2.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg2 must be numeric')
1068
1203
 
1069
- arg2.zero? and return Expected::Failure(:division_by_zero, 'arg2 must not be zero')
1204
+ arg2.zero? and return Result::Failure(:division_by_zero, 'arg2 must not be zero')
1070
1205
 
1071
- Expected::Success(:division_completed, arg1 / arg2)
1206
+ Result::Success(:division_completed, arg1 / arg2)
1072
1207
  end
1073
1208
  end
1074
1209
 
1075
1210
  Divide.call('4', 2)
1076
1211
  # type :invalid_arg is not allowed. Allowed types: :err
1077
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1212
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1078
1213
 
1079
1214
  Divide.call(4, 2)
1080
1215
  # type :division_completed is not allowed. Allowed types: :ok
1081
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1216
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1082
1217
  ```
1083
1218
 
1084
1219
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -1119,7 +1254,7 @@ end
1119
1254
 
1120
1255
  ```ruby
1121
1256
  module Divide
1122
- Expected = BCDD::Result::Expectations.new(
1257
+ Result = BCDD::Result::Expectations.new(
1123
1258
  success: {
1124
1259
  numbers: ->(value) { value.is_a?(Array) && value.size == 2 && value.all?(Numeric) },
1125
1260
  division_completed: Numeric
@@ -1131,12 +1266,12 @@ module Divide
1131
1266
  )
1132
1267
 
1133
1268
  def self.call(arg1, arg2)
1134
- arg1.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg1 must be numeric')
1135
- arg2.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg2 must be numeric')
1269
+ arg1.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg1 must be numeric')
1270
+ arg2.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg2 must be numeric')
1136
1271
 
1137
- arg2.zero? and return Expected::Failure(:division_by_zero, 'arg2 must not be zero')
1272
+ arg2.zero? and return Result::Failure(:division_by_zero, 'arg2 must not be zero')
1138
1273
 
1139
- Expected::Success(:division_completed, arg1 / arg2)
1274
+ Result::Success(:division_completed, arg1 / arg2)
1140
1275
  end
1141
1276
  end
1142
1277
  ```
@@ -1148,50 +1283,86 @@ The value validation will only be performed through the methods `Success()` and
1148
1283
  ##### Success()
1149
1284
 
1150
1285
  ```ruby
1151
- Divide::Expected::Success(:ok)
1152
- # type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Expectations::Error::UnexpectedType)
1286
+ Divide::Result::Success(:ok)
1287
+ # type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
1153
1288
 
1154
- Divide::Expected::Success(:numbers, [1])
1155
- # value [1] is not allowed for :numbers type (BCDD::Result::Expectations::Error::UnexpectedValue)
1289
+ Divide::Result::Success(:numbers, [1])
1290
+ # value [1] is not allowed for :numbers type (BCDD::Result::Contract::Error::UnexpectedValue)
1156
1291
 
1157
- Divide::Expected::Success(:division_completed, '2')
1158
- # value "2" is not allowed for :division_completed type (BCDD::Result::Expectations::Error::UnexpectedValue)
1292
+ Divide::Result::Success(:division_completed, '2')
1293
+ # value "2" is not allowed for :division_completed type (BCDD::Result::Contract::Error::UnexpectedValue)
1159
1294
  ```
1160
1295
 
1161
1296
  ##### Failure()
1162
1297
 
1163
1298
  ```ruby
1164
- Divide::Expected::Failure(:err)
1165
- # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Expectations::Error::UnexpectedType)
1299
+ Divide::Result::Failure(:err)
1300
+ # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
1301
+
1302
+ Divide::Result::Failure(:invalid_arg, :arg1_must_be_numeric)
1303
+ # value :arg1_must_be_numeric is not allowed for :invalid_arg type (BCDD::Result::Contract::Error::UnexpectedValue)
1304
+
1305
+ Divide::Result::Failure(:division_by_zero, msg: 'arg2 must not be zero')
1306
+ # value {:msg=>"arg2 must not be zero"} is not allowed for :division_by_zero type (BCDD::Result::Contract::Error::UnexpectedValue)
1307
+ ```
1308
+
1309
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1310
+
1311
+ ##### Pattern Matching Support
1312
+
1313
+ The value checking has support for handling pattern-matching errors, and the cleanest way to do it is using the one-line pattern matching operators (`=>` since Ruby 3.0) and (`in` Ruby 2.7).
1314
+
1315
+ How does this operator work? They raise an error when the pattern does not match but returns nil when it matches.
1316
+
1317
+ Because of this, you will need to enable `nil` as a valid value checking. You can do it through the `BCDD::Result.configuration` or by allowing it directly on the mixin config.
1318
+
1319
+ ```ruby
1320
+ module Divide
1321
+ extend BCDD::Result::Expectations.mixin(
1322
+ config: {
1323
+ pattern_matching: { nil_as_valid_value_checking: true }
1324
+ },
1325
+ success: {
1326
+ division_completed: ->(value) { value => (Integer | Float) }
1327
+ },
1328
+ failure: { invalid_arg: String, division_by_zero: String }
1329
+ )
1330
+
1331
+ def self.call(arg1, arg2)
1332
+ arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
1333
+ arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
1334
+
1335
+ arg2.zero? and return Failure(:division_by_zero, 'arg2 must not be zero')
1166
1336
 
1167
- Divide::Expected::Failure(:invalid_arg, :arg1_must_be_numeric)
1168
- # value :arg1_must_be_numeric is not allowed for :invalid_arg type (BCDD::Result::Expectations::Error::UnexpectedValue)
1337
+ Success(:division_completed, String(arg1 / arg2))
1338
+ end
1339
+ end
1169
1340
 
1170
- Divide::Expected::Failure(:division_by_zero, msg: 'arg2 must not be zero')
1171
- # value {:msg=>"arg2 must not be zero"} is not allowed for :division_by_zero type (BCDD::Result::Expectations::Error::UnexpectedValue)
1341
+ Divide.call(10, 5)
1342
+ # value "2" is not allowed for :division_completed type ("2": Float === "2" does not return true) (BCDD::Result::Contract::Error::UnexpectedValue)
1172
1343
  ```
1173
1344
 
1174
1345
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1175
1346
 
1176
- #### `BCDD::Result::Expectations.mixin` Addons
1347
+ #### `BCDD::Result::Expectations.mixin` add-ons
1177
1348
 
1178
- The `BCDD::Result::Expectations.mixin` also accepts the `with:` argument. It is a hash that will be used to define the methods that will be added to the target object.
1349
+ The `BCDD::Result::Expectations.mixin` also accepts the `config:` argument. It is a hash that can be used to define custom behaviors for the mixin.
1179
1350
 
1180
1351
  **Continue**
1181
1352
 
1182
- It is similar to `BCDD::Result.mixin(with: :Continue)`, the key difference is that the `Continue(value)` will be ignored by the expectations. This is extremely useful when you want to use `Continue(value)` to chain operations, but you don't want to declare N success types in the expectations.
1353
+ It is similar to `BCDD::Result.mixin(config: { addon: { continue: true } })`, the key difference is that the `Continue(value)` will be ignored by the expectations. This is extremely useful when you want to use `Continue(value)` to chain operations, but you don't want to declare N success types in the expectations.
1183
1354
 
1184
1355
  ```ruby
1185
1356
  class Divide
1186
1357
  include BCDD::Result::Expectations.mixin(
1187
- with: :Continue,
1358
+ config: { addon: { continue: true } },
1188
1359
  success: :division_completed,
1189
1360
  failure: %i[invalid_arg division_by_zero]
1190
1361
  )
1191
1362
 
1192
1363
  def call(arg1, arg2)
1193
1364
  validate_numbers(arg1, arg2)
1194
- .and_then(:validate_non_zero)
1365
+ .and_then(:validate_nonzero)
1195
1366
  .and_then(:divide)
1196
1367
  end
1197
1368
 
@@ -1204,7 +1375,7 @@ class Divide
1204
1375
  Continue([arg1, arg2])
1205
1376
  end
1206
1377
 
1207
- def validate_non_zero(numbers)
1378
+ def validate_nonzero(numbers)
1208
1379
  return Continue(numbers) unless numbers.last.zero?
1209
1380
 
1210
1381
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -1215,7 +1386,7 @@ class Divide
1215
1386
  end
1216
1387
  end
1217
1388
 
1218
- result = Divide.new.call(4,2)
1389
+ result = Divide.new.call(4, 2)
1219
1390
  # => #<BCDD::Result::Success type=:division_completed value=2>
1220
1391
 
1221
1392
  # The example below shows an error because the :ok type is not allowed.
@@ -1223,11 +1394,447 @@ result = Divide.new.call(4,2)
1223
1394
  # This is because the :continued type is ignored by the expectations.
1224
1395
  #
1225
1396
  result.success?(:ok)
1226
- # type :ok is not allowed. Allowed types: :division_completed (BCDD::Result::Expectations::Error::UnexpectedType)
1397
+ # type :ok is not allowed. Allowed types: :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
1398
+ ```
1399
+
1400
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1401
+
1402
+ ### `BCDD::Result::Context`
1403
+
1404
+ The `BCDD::Result::Context` is a `BCDD::Result`, meaning it has all the features of the `BCDD::Result`. The main difference is that it only accepts keyword arguments as a value, which applies to the `and_then`: The called methods must receive keyword arguments, and the dependency injection will be performed through keyword arguments.
1405
+
1406
+ As the input/output are hashes, the results of each `and_then` call will automatically accumulate. This is useful in operations chaining, as the result of the previous operations will be automatically available for the next one. Because of this behavior, the `BCDD::Result::Context` has the `#and_expose` method to expose only the desired keys from the accumulated result.
1407
+
1408
+ #### Defining successes and failures
1409
+
1410
+ As the `BCDD::Result`, you can declare success and failures directly from `BCDD::Result::Context`.
1411
+
1412
+ ```ruby
1413
+ BCDD::Result::Context::Success(:ok, a: 1, b: 2)
1414
+ #<BCDD::Result::Context::Success type=:ok value={:a=>1, :b=>2}>
1415
+
1416
+ BCDD::Result::Context::Failure(:err, message: 'something went wrong')
1417
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"something went wrong"}>
1418
+ ```
1419
+
1420
+ But different from `BCDD::Result` that accepts any value, the `BCDD::Result::Context` only takes keyword arguments.
1421
+
1422
+ ```ruby
1423
+ BCDD::Result::Context::Success(:ok, [1, 2])
1424
+ # wrong number of arguments (given 2, expected 1) (ArgumentError)
1425
+
1426
+ BCDD::Result::Context::Failure(:err, { message: 'something went wrong' })
1427
+ # wrong number of arguments (given 2, expected 1) (ArgumentError)
1428
+
1429
+ #
1430
+ # Use ** to convert a hash to keyword arguments
1431
+ #
1432
+ BCDD::Result::Context::Success(:ok, **{ message: 'hashes can be converted to keyword arguments' })
1433
+ #<BCDD::Result::Context::Success type=:ok value={:message=>"hashes can be converted to keyword arguments"}>
1434
+ ```
1435
+
1436
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1437
+
1438
+ #### `BCDD::Result::Context.mixin`
1439
+
1440
+ As in the `BCDD::Result`, you can use the `BCDD::Result::Context.mixin` to add the `Success()` and `Failure()` methods to your classes/modules.
1441
+
1442
+ Let's see this feature and the data accumulation in action:
1443
+
1444
+ ##### Class example (Instance Methods)
1445
+
1446
+ ```ruby
1447
+ require 'logger'
1448
+
1449
+ class Divide
1450
+ include BCDD::Result::Context.mixin
1451
+
1452
+ def call(arg1, arg2, logger: ::Logger.new(STDOUT))
1453
+ validate_numbers(arg1, arg2)
1454
+ .and_then(:validate_nonzero)
1455
+ .and_then(:divide, logger: logger)
1456
+ end
1457
+
1458
+ private
1459
+
1460
+ def validate_numbers(arg1, arg2)
1461
+ arg1.is_a?(::Numeric) or return Failure(:err, message: 'arg1 must be numeric')
1462
+ arg2.is_a?(::Numeric) or return Failure(:err, message: 'arg2 must be numeric')
1463
+
1464
+ Success(:ok, number1: arg1, number2: arg2)
1465
+ end
1466
+
1467
+ def validate_nonzero(number2:, **)
1468
+ return Success(:ok) if number2.nonzero?
1469
+
1470
+ Failure(:err, message: 'arg2 must not be zero')
1471
+ end
1472
+
1473
+ #
1474
+ # The logger was injected via #and_then and keyword arguments
1475
+ #
1476
+ def divide(number1:, number2:, logger:)
1477
+ result = number1 / number2
1478
+
1479
+ logger.info("The division result is #{result}")
1480
+
1481
+ Success(:ok, number: result)
1482
+ end
1483
+ end
1484
+
1485
+ Divide.new.call(10, 5)
1486
+ # I, [2023-10-27T01:51:46.905004 #76915] INFO -- : The division result is 2
1487
+ #<BCDD::Result::Context::Success type=:ok value={:number=>2}>
1488
+
1489
+ Divide.new.call('10', 5)
1490
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"arg1 must be numeric"}>
1491
+
1492
+ Divide.new.call(10, '5')
1493
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"arg2 must be numeric"}>
1494
+
1495
+ Divide.new.call(10, 0)
1496
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"arg2 must not be zero"}>
1497
+ ```
1498
+
1499
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1500
+
1501
+ ##### `and_expose`
1502
+
1503
+ This allows you to expose only the desired keys from the accumulated result. It can be used with any `BCDD::Result::Context` object.
1504
+
1505
+ Let's add it to the previous example:
1506
+
1507
+ ```ruby
1508
+ class Divide
1509
+ include BCDD::Result::Context.mixin
1510
+
1511
+ def call(arg1, arg2)
1512
+ validate_numbers(arg1, arg2)
1513
+ .and_then(:validate_nonzero)
1514
+ .and_then(:divide)
1515
+ .and_expose(:division_completed, [:number])
1516
+ end
1517
+
1518
+ private
1519
+
1520
+ def validate_numbers(arg1, arg2)
1521
+ arg1.is_a?(::Numeric) or return Failure(:err, message: 'arg1 must be numeric')
1522
+ arg2.is_a?(::Numeric) or return Failure(:err, message: 'arg2 must be numeric')
1523
+
1524
+ Success(:ok, number1: arg1, number2: arg2)
1525
+ end
1526
+
1527
+ def validate_nonzero(number2:, **)
1528
+ return Success(:ok) if number2.nonzero?
1529
+
1530
+ Failure(:err, message: 'arg2 must not be zero')
1531
+ end
1532
+
1533
+ def divide(**input)
1534
+ Success(:ok, number: input.values.reduce(:/), **input)
1535
+ end
1536
+ end
1537
+
1538
+ Divide.new.call(10, 5)
1539
+ #<BCDD::Result::Context::Success type=:division_completed value={:number=>2}>
1540
+ ```
1541
+
1542
+ As you can see, even with `divide` success exposing the division number with all the accumulated data (`**input`), the `#and_expose` could generate a new success with a new type and only with the desired keys.
1543
+
1544
+ Remove the `#and_expose` call to see the difference. This will be the outcome:
1545
+
1546
+ ```ruby
1547
+ Divide.new.call(10, 5)
1548
+ #<BCDD::Result::Context::Success type=:ok value={:number=>2, :number1=>10, :number2=>5}>
1549
+ ```
1550
+
1551
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1552
+
1553
+ ##### Module example (Singleton Methods)
1554
+
1555
+ Yes, the `BCDD::Result::Context.mixin` can produce singleton methods. Look for an example using a module (but it could be a class, too).
1556
+
1557
+ ```ruby
1558
+ module Divide
1559
+ extend self, BCDD::Result::Context.mixin
1560
+
1561
+ def call(arg1, arg2)
1562
+ validate_numbers(arg1, arg2)
1563
+ .and_then(:validate_nonzero)
1564
+ .and_then(:divide)
1565
+ .and_expose(:division_completed, [:number])
1566
+ end
1567
+
1568
+ private
1569
+
1570
+ def validate_numbers(arg1, arg2)
1571
+ arg1.is_a?(::Numeric) or return Failure(:err, message: 'arg1 must be numeric')
1572
+ arg2.is_a?(::Numeric) or return Failure(:err, message: 'arg2 must be numeric')
1573
+
1574
+ Success(:ok, number1: arg1, number2: arg2)
1575
+ end
1576
+
1577
+ def validate_nonzero(number2:, **)
1578
+ return Success(:ok) if number2.nonzero?
1579
+
1580
+ Failure(:err, message: 'arg2 must not be zero')
1581
+ end
1582
+
1583
+ def divide(number1:, number2:)
1584
+ Success(:ok, number: number1 / number2)
1585
+ end
1586
+ end
1587
+
1588
+ Divide.call(10, 5)
1589
+ #<BCDD::Result::Context::Success type=:division_completed value={:number=>2}>
1590
+
1591
+ Divide.call('10', 5)
1592
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"arg1 must be numeric"}>
1593
+
1594
+ Divide.call(10, '5')
1595
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"arg2 must be numeric"}>
1596
+
1597
+ Divide.call(10, 0)
1598
+ #<BCDD::Result::Context::Failure type=:err value={:message=>"arg2 must not be zero"}>
1599
+ ```
1600
+
1601
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1602
+
1603
+ #### `BCDD::Result::Context::Expectations`
1604
+
1605
+ The `BCDD::Result::Context::Expectations` is a `BCDD::Result::Expectations` with the `BCDD::Result::Context` features.
1606
+
1607
+ This is an example using the mixin mode, but the standalone mode is also supported.
1608
+
1609
+ ```ruby
1610
+ class Divide
1611
+ include BCDD::Result::Context::Expectations.mixin(
1612
+ config: {
1613
+ pattern_matching: { nil_as_valid_value_checking: true }
1614
+ },
1615
+ success: {
1616
+ division_completed: ->(value) { value => { number: Numeric } }
1617
+ },
1618
+ failure: {
1619
+ invalid_arg: ->(value) { value => { message: String } },
1620
+ division_by_zero: ->(value) { value => { message: String } }
1621
+ }
1622
+ )
1623
+
1624
+ def call(arg1, arg2)
1625
+ arg1.is_a?(Numeric) or return Failure(:invalid_arg, message: 'arg1 must be numeric')
1626
+ arg2.is_a?(Numeric) or return Failure(:invalid_arg, message: 'arg2 must be numeric')
1627
+
1628
+ arg2.zero? and return Failure(:division_by_zero, message: 'arg2 must not be zero')
1629
+
1630
+ Success(:division_completed, number: (arg1 / arg2))
1631
+ end
1632
+ end
1633
+
1634
+ Divide.new.call(10, 5)
1635
+ #<BCDD::Result::Context::Success type=:division_completed value={:number=>2}>
1636
+ ```
1637
+
1638
+ As in the `BCDD::Result::Expectations.mixin`, the `BCDD::Result::Context::Expectations.mixin` will add a Result constant in the target class. It can generate success/failure results, which ensure the mixin expectations.
1639
+
1640
+ Let's see this using previous example:
1641
+
1642
+ ```ruby
1643
+ Divide::Result::Success(:division_completed, number: 2)
1644
+ #<BCDD::Result::Context::Success type=:division_completed value={:number=>2}>
1645
+
1646
+ Divide::Result::Success(:division_completed, number: '2')
1647
+ # value {:number=>"2"} is not allowed for :division_completed type ({:number=>"2"}: Numeric === "2" does not return true) (BCDD::Result::Contract::Error::UnexpectedValue)
1648
+ ```
1649
+
1650
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1651
+
1652
+ #### Mixin add-ons
1653
+
1654
+ The `BCDD::Result::Context.mixin` and `BCDD::Result::Context::Expectations.mixin` also accepts the `config:` argument. And it works the same way as the `BCDD::Result` mixins.
1655
+
1656
+ **Continue**
1657
+
1658
+ The `BCDD::Result::Context.mixin(config: { addon: { continue: true } })` or `BCDD::Result::Context::Expectations.mixin(config: { addon: { continue: true } })` adds a `Continue(**input)` that will be ignored by the expectations. This is extremely useful when you want to use `Continue()` to chain operations, but you don't want to declare N success types in the expectations.
1659
+
1660
+ Let's use a mix of `BCDD::Result::Context` features to see in action with this add-on:
1661
+
1662
+ ```ruby
1663
+ module Divide
1664
+ require 'logger'
1665
+
1666
+ extend self, BCDD::Result::Context::Expectations.mixin(
1667
+ config: {
1668
+ addon: { continue: true },
1669
+ pattern_matching: { nil_as_valid_value_checking: true }
1670
+ },
1671
+ success: {
1672
+ division_completed: ->(value) { value => { number: Numeric } }
1673
+ },
1674
+ failure: {
1675
+ invalid_arg: ->(value) { value => { message: String } },
1676
+ division_by_zero: ->(value) { value => { message: String } }
1677
+ }
1678
+ )
1679
+
1680
+ def call(arg1, arg2, logger: ::Logger.new(STDOUT))
1681
+ validate_numbers(arg1, arg2)
1682
+ .and_then(:validate_nonzero)
1683
+ .and_then(:divide, logger: logger)
1684
+ .and_expose(:division_completed, [:number])
1685
+ end
1686
+
1687
+ private
1688
+
1689
+ def validate_numbers(arg1, arg2)
1690
+ arg1.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg1 must be numeric')
1691
+ arg2.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg2 must be numeric')
1692
+
1693
+ Continue(number1: arg1, number2: arg2)
1694
+ end
1695
+
1696
+ def validate_nonzero(number2:, **)
1697
+ return Continue() if number2.nonzero?
1698
+
1699
+ Failure(:division_by_zero, message: 'arg2 must not be zero')
1700
+ end
1701
+
1702
+ def divide(number1:, number2:, logger:)
1703
+ result = number1 / number2
1704
+
1705
+ logger.info("The division result is #{result}")
1706
+
1707
+ Continue(number: result)
1708
+ end
1709
+ end
1710
+
1711
+ Divide.call(14, 2)
1712
+ # I, [2023-10-27T02:01:05.812388 #77823] INFO -- : The division result is 7
1713
+ #<BCDD::Result::Context::Success type=:division_completed value={:number=>7}>
1714
+
1715
+ Divide.call('14', 2)
1716
+ #<BCDD::Result::Context::Failure type=:invalid_arg value={:message=>"arg1 must be numeric"}>
1717
+
1718
+ Divide.call(14, '2')
1719
+ #<BCDD::Result::Context::Failure type=:invalid_arg value={:message=>"arg2 must be numeric"}>
1720
+
1721
+ Divide.call(14, 0)
1722
+ #<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
1227
1723
  ```
1228
1724
 
1725
+ ### `BCDD::Result.configuration`
1726
+
1727
+ The `BCDD::Result.configuration` allows you to configure default behaviors for `BCDD::Result` and `BCDD::Result::Context` through a configuration block. After using it, the configuration is frozen, ensuring the expected behaviors for your application.
1728
+
1729
+ ```ruby
1730
+ BCDD::Result.configuration do |config|
1731
+ config.addon.enable!(:continue)
1732
+
1733
+ config.constant_alias.enable!('Result')
1734
+
1735
+ config.pattern_matching.disable!(:nil_as_valid_value_checking)
1736
+
1737
+ config.feature.disable!(:expectations) if ::Rails.env.production?
1738
+ end
1739
+ ```
1740
+
1741
+ Use `disable!` to disable a feature and `enable!` to enable it.
1742
+
1743
+ Let's see what each configuration in the example above does:
1744
+
1745
+ #### `config.addon.enable!(:continue)`
1746
+
1747
+ This configuration enables the `Continue()` method for `BCDD::Result` and `BCDD::Result::Context`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).
1748
+
1749
+ #### `config.constant_alias.enable!('Result')`
1750
+
1751
+ This configuration make `Result` a constant alias for `BCDD::Result`. Link to [documentation](#bcddresult-versus-result).
1752
+
1753
+ #### `config.pattern_matching.disable!(:nil_as_valid_value_checking)`
1754
+
1755
+ This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::Result::Context`. Link to [documentation](#pattern-matching-support).
1756
+
1229
1757
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1230
1758
 
1759
+ #### `config.feature.disable!(:expectations)`
1760
+
1761
+ This configuration turns off the expectations for `BCDD::Result` and `BCDD::Result::Context`. The expectations are helpful in development and test environments, but they can be disabled in production environments for performance gain.
1762
+
1763
+ PS: I'm using `::Rails.env.production?` to check the environment, but you can use any logic you want.
1764
+
1765
+ ### `BCDD::Result.config`
1766
+
1767
+ The `BCDD::Result.config` allows you to access the current configuration. It is useful when you want to check the current configuration.
1768
+
1769
+ **BCDD::Result.config.addon**
1770
+
1771
+ ```ruby
1772
+ BCDD::Result.config.addon.enabled?(:continue)
1773
+
1774
+ BCDD::Result.config.addon.options
1775
+ # {
1776
+ # :continue=>{
1777
+ # :enabled=>false,
1778
+ # :affects=>[
1779
+ # "BCDD::Result",
1780
+ # "BCDD::Result::Context",
1781
+ # "BCDD::Result::Expectations",
1782
+ # "BCDD::Result::Context::Expectations"
1783
+ # ]
1784
+ # }
1785
+ # }
1786
+ ```
1787
+
1788
+ **BCDD::Result.config.constant_alias**
1789
+
1790
+ ```ruby
1791
+ BCDD::Result.config.constant_alias.enabled?('Result')
1792
+
1793
+ BCDD::Result.config.constant_alias.options
1794
+ # {
1795
+ # "Result"=>{
1796
+ # :enabled=>false,
1797
+ # :affects=>[
1798
+ # "Object"
1799
+ # ]
1800
+ # }
1801
+ # }
1802
+ ```
1803
+
1804
+ **BCDD::Result.config.pattern_matching**
1805
+
1806
+ ```ruby
1807
+ BCDD::Result.config.pattern_matching.enabled?(:nil_as_valid_value_checking)
1808
+
1809
+ BCDD::Result.config.pattern_matching.options
1810
+ # {
1811
+ # :nil_as_valid_value_checking=>{
1812
+ # :enabled=>false,
1813
+ # :affects=>[
1814
+ # "BCDD::Result::Expectations,
1815
+ # "BCDD::Result::Context::Expectations"
1816
+ # ]
1817
+ # }
1818
+ # }
1819
+ ```
1820
+
1821
+ **BCDD::Result.config.feature**
1822
+
1823
+ ```ruby
1824
+ BCDD::Result.config.feature.enabled?(:expectations)
1825
+
1826
+ BCDD::Result.config.feature.options
1827
+ # {
1828
+ # :expectations=>{
1829
+ # :enabled=>true,
1830
+ # :affects=>[
1831
+ # "BCDD::Result::Expectations,
1832
+ # "BCDD::Result::Context::Expectations"
1833
+ # ]
1834
+ # }
1835
+ # }
1836
+ ```
1837
+
1231
1838
  ## About
1232
1839
 
1233
1840
  [Rodrigo Serradura](https://github.com/serradura) created this project. He is the B/CDD process/method creator and has already made similar gems like the [u-case](https://github.com/serradura/u-case) and [kind](https://github.com/serradura/kind/blob/main/lib/kind/result.rb). This gem is a general-purpose abstraction/monad, but it also contains key features that serve as facilitators for adopting B/CDD in the code.