bcdd-result 0.7.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +142 -0
- data/README.md +352 -144
- data/Rakefile +9 -3
- data/Steepfile +1 -1
- data/lib/bcdd/result/config/constant_alias.rb +35 -0
- data/lib/bcdd/result/config/options.rb +27 -0
- data/lib/bcdd/result/config/switcher.rb +82 -0
- data/lib/bcdd/result/config.rb +71 -0
- data/lib/bcdd/result/context/expectations/mixin.rb +5 -17
- data/lib/bcdd/result/context/expectations.rb +9 -25
- data/lib/bcdd/result/context/mixin.rb +23 -12
- data/lib/bcdd/result/context.rb +1 -1
- data/lib/bcdd/result/contract/evaluator.rb +1 -1
- data/lib/bcdd/result/contract/for_types_and_values.rb +7 -2
- data/lib/bcdd/result/contract.rb +4 -14
- data/lib/bcdd/result/data.rb +7 -7
- data/lib/bcdd/result/expectations/mixin.rb +30 -20
- data/lib/bcdd/result/expectations.rb +47 -13
- data/lib/bcdd/result/failure/methods.rb +1 -1
- data/lib/bcdd/result/mixin.rb +39 -18
- data/lib/bcdd/result/success/methods.rb +1 -1
- data/lib/bcdd/result/version.rb +1 -1
- data/lib/bcdd/result.rb +22 -6
- data/lib/bcdd-result.rb +3 -0
- data/sig/bcdd/result.rbs +174 -48
- metadata +11 -8
- 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
|
3
|
+
<p align="center"><i>Empower Ruby apps with pragmatic use of Result pattern (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
|
-
- [
|
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)
|
@@ -63,12 +63,19 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
|
|
63
63
|
- [`BCDD::Result::Expectations.mixin` add-ons](#bcddresultexpectationsmixin-add-ons)
|
64
64
|
- [`BCDD::Result::Context`](#bcddresultcontext)
|
65
65
|
- [Defining successes and failures](#defining-successes-and-failures)
|
66
|
+
- [Constant aliases](#constant-aliases)
|
66
67
|
- [`BCDD::Result::Context.mixin`](#bcddresultcontextmixin)
|
67
68
|
- [Class example (Instance Methods)](#class-example-instance-methods-1)
|
68
69
|
- [`and_expose`](#and_expose)
|
69
70
|
- [Module example (Singleton Methods)](#module-example-singleton-methods-1)
|
70
71
|
- [`BCDD::Result::Context::Expectations`](#bcddresultcontextexpectations)
|
71
72
|
- [Mixin add-ons](#mixin-add-ons)
|
73
|
+
- [`BCDD::Result.configuration`](#bcddresultconfiguration)
|
74
|
+
- [`config.addon.enable!(:continue)`](#configaddonenablecontinue)
|
75
|
+
- [`config.constant_alias.enable!('Result', 'BCDD::Context')`](#configconstant_aliasenableresult-bcddcontext)
|
76
|
+
- [`config.pattern_matching.disable!(:nil_as_valid_value_checking)`](#configpattern_matchingdisablenil_as_valid_value_checking)
|
77
|
+
- [`config.feature.disable!(:expectations)`](#configfeaturedisableexpectations)
|
78
|
+
- [`BCDD::Result.config`](#bcddresultconfig)
|
72
79
|
- [About](#about)
|
73
80
|
- [Development](#development)
|
74
81
|
- [Contributing](#contributing)
|
@@ -84,7 +91,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
|
|
84
91
|
Add this line to your application's Gemfile:
|
85
92
|
|
86
93
|
```ruby
|
87
|
-
gem 'bcdd-result'
|
94
|
+
gem 'bcdd-result'
|
88
95
|
```
|
89
96
|
|
90
97
|
And then execute:
|
@@ -95,6 +102,10 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
95
102
|
|
96
103
|
$ gem install bcdd-result
|
97
104
|
|
105
|
+
And require it in your code:
|
106
|
+
|
107
|
+
require 'bcdd/result'
|
108
|
+
|
98
109
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
99
110
|
|
100
111
|
## Usage
|
@@ -119,17 +130,27 @@ BCDD::Result::Failure(:err) #
|
|
119
130
|
|
120
131
|
#### `BCDD::Result` *versus* `Result`
|
121
132
|
|
122
|
-
|
133
|
+
This gem provides a way to create constant aliases for `BCDD::Result` and other classes/modules.
|
123
134
|
|
124
|
-
|
135
|
+
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.
|
125
136
|
|
126
137
|
```ruby
|
127
|
-
|
138
|
+
BCDD::Result.configuration do |config|
|
139
|
+
config.constant_alias.enable!('Result')
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
So, instead of using `BCDD::Result` everywhere, you can use `Result` as an alias/shortcut.
|
128
144
|
|
145
|
+
```ruby
|
129
146
|
Result::Success(:ok) # <BCDD::Result::Success type=:ok value=nil>
|
147
|
+
|
148
|
+
Result::Failure(:err) # <BCDD::Result::Failure type=:err value=nil>
|
130
149
|
```
|
131
150
|
|
132
|
-
|
151
|
+
If you have enabled constant aliasing, all examples in this README that use `BCDD::Result` can be implemented using `Result`.
|
152
|
+
|
153
|
+
There are other aliases and configurations available. Check the [BCDD::Result.configuration]() section for more information.
|
133
154
|
|
134
155
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
135
156
|
|
@@ -169,12 +190,12 @@ result.value # nil
|
|
169
190
|
################
|
170
191
|
# With a value #
|
171
192
|
################
|
172
|
-
result = BCDD::Result::Failure(:err,
|
193
|
+
result = BCDD::Result::Failure(:err, 'my_value')
|
173
194
|
|
174
195
|
result.success? # false
|
175
196
|
result.failure? # true
|
176
197
|
result.type # :err
|
177
|
-
result.value #
|
198
|
+
result.value # "my_value"
|
178
199
|
|
179
200
|
###################
|
180
201
|
# Without a value #
|
@@ -187,9 +208,11 @@ result.type # :no
|
|
187
208
|
result.value # nil
|
188
209
|
```
|
189
210
|
|
211
|
+
In both cases, the `type` must be a symbol, and the `value` can be any kind of object.
|
212
|
+
|
190
213
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
191
214
|
|
192
|
-
####
|
215
|
+
#### Checking types with `result.success?` or `result.failure?`
|
193
216
|
|
194
217
|
`BCDD::Result#success?` and `BCDD::Result#failure?` are methods that allow you to check if the result is a success or a failure.
|
195
218
|
|
@@ -198,9 +221,11 @@ You can also check the result type by passing an argument to it. For example, `r
|
|
198
221
|
```ruby
|
199
222
|
result = BCDD::Result::Success(:ok)
|
200
223
|
|
201
|
-
result.success?
|
202
|
-
|
203
|
-
|
224
|
+
result.success?(:ok)
|
225
|
+
|
226
|
+
# This is the same as:
|
227
|
+
|
228
|
+
result.success? && result.type == :ok
|
204
229
|
```
|
205
230
|
|
206
231
|
The same is valid for `BCDD::Result#failure?`.
|
@@ -208,9 +233,11 @@ The same is valid for `BCDD::Result#failure?`.
|
|
208
233
|
```ruby
|
209
234
|
result = BCDD::Result::Failure(:err)
|
210
235
|
|
211
|
-
result.failure?
|
212
|
-
|
213
|
-
|
236
|
+
result.failure?(:err)
|
237
|
+
|
238
|
+
# This is the same as:
|
239
|
+
|
240
|
+
result.failure? && result.type == :err
|
214
241
|
```
|
215
242
|
|
216
243
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
@@ -218,7 +245,7 @@ result.failure?(:error) # false
|
|
218
245
|
### Result Hooks
|
219
246
|
|
220
247
|
Result hooks are methods that allow you to execute a block of code based on the type of result obtained.
|
221
|
-
To demonstrate their use, I will implement a
|
248
|
+
To demonstrate their use, I will implement a method that can divide two numbers.
|
222
249
|
|
223
250
|
```ruby
|
224
251
|
def divide(arg1, arg2)
|
@@ -282,8 +309,7 @@ result = divide(nil, 2)
|
|
282
309
|
|
283
310
|
output =
|
284
311
|
result
|
285
|
-
.on_type(:invalid_arg) { |msg| puts msg }
|
286
|
-
.on_type(:division_by_zero) { |msg| puts msg }
|
312
|
+
.on_type(:invalid_arg, :division_by_zero) { |msg| puts msg }
|
287
313
|
.on_type(:division_completed) { |number| puts number }
|
288
314
|
|
289
315
|
# The code above will print 'arg1 must be numeric' and return the result itself.
|
@@ -303,16 +329,18 @@ The `BCDD::Result#on_success` method is quite similar to the `BCDD::Result#on` h
|
|
303
329
|
2. If the type declaration is not included, the method will execute the block for any successful result, regardless of its type.
|
304
330
|
|
305
331
|
```ruby
|
306
|
-
#
|
332
|
+
# In both examples, it executes the block and returns the result itself.
|
307
333
|
|
308
334
|
divide(4, 2).on_success { |number| puts number }
|
309
335
|
|
310
336
|
divide(4, 2).on_success(:division_completed) { |number| puts number }
|
311
337
|
|
312
|
-
# It doesn't execute the block
|
338
|
+
# It doesn't execute the block as the type is different.
|
313
339
|
|
314
340
|
divide(4, 4).on_success(:ok) { |value| puts value }
|
315
341
|
|
342
|
+
# It doesn't execute the block, as the result is a success, but the hook expects a failure.
|
343
|
+
|
316
344
|
divide(4, 4).on_failure { |error| puts error }
|
317
345
|
```
|
318
346
|
|
@@ -328,17 +356,19 @@ It is the opposite of `Result#on_success`:
|
|
328
356
|
2. If the type declaration is not included, the method will execute the block for any failed result, regardless of its type.
|
329
357
|
|
330
358
|
```ruby
|
331
|
-
#
|
359
|
+
# In both examples, it executes the block and returns the result itself.
|
332
360
|
|
333
361
|
divide(nil, 2).on_failure { |error| puts error }
|
334
362
|
|
335
|
-
divide(4, 0).on_failure(:
|
363
|
+
divide(4, 0).on_failure(:division_by_zero) { |error| puts error }
|
336
364
|
|
337
|
-
# It doesn't execute the block
|
338
|
-
|
339
|
-
divide(4, 0).on_success { |number| puts number }
|
365
|
+
# It doesn't execute the block as the type is different.
|
340
366
|
|
341
367
|
divide(4, 0).on_failure(:invalid_arg) { |error| puts error }
|
368
|
+
|
369
|
+
# It doesn't execute the block, as the result is a failure, but the hook expects a success.
|
370
|
+
|
371
|
+
divide(4, 0).on_success { |number| puts number }
|
342
372
|
```
|
343
373
|
|
344
374
|
*PS: The `divide()` implementation is [here](#result-hooks).*
|
@@ -453,7 +483,7 @@ divide(100, 0).value_or { 0 } # 0
|
|
453
483
|
|
454
484
|
#### `result.data`
|
455
485
|
|
456
|
-
The `BCDD::Result#data` exposes the result attributes (
|
486
|
+
The `BCDD::Result#data` exposes the result attributes (kind, type, value) directly and as a hash (`to_h`/`to_hash`) and array (`to_a`/`to_ary`).
|
457
487
|
|
458
488
|
This is helpful if you need to access the result attributes generically or want to use Ruby features like splat (`*`) and double splat (`**`) operators.
|
459
489
|
|
@@ -462,25 +492,25 @@ See the examples below to understand how to use it.
|
|
462
492
|
```ruby
|
463
493
|
result = BCDD::Result::Success(:ok, 1)
|
464
494
|
|
465
|
-
success_data = result.data # #<BCDD::Result::Data
|
495
|
+
success_data = result.data # #<BCDD::Result::Data kind=:success type=:ok value=1>
|
466
496
|
|
467
|
-
success_data.
|
497
|
+
success_data.kind # :success
|
468
498
|
success_data.type # :ok
|
469
499
|
success_data.value # 1
|
470
500
|
|
471
|
-
success_data.to_h # {:
|
501
|
+
success_data.to_h # {:kind=>:success, :type=>:ok, :value=>1}
|
472
502
|
success_data.to_a # [:success, :ok, 1]
|
473
503
|
|
474
|
-
|
504
|
+
kind, type, value = success_data
|
475
505
|
|
476
|
-
[
|
506
|
+
[kind, type, value] # [:success, :ok, 1]
|
477
507
|
|
478
|
-
def print_to_ary(
|
479
|
-
puts [
|
508
|
+
def print_to_ary(kind, type, value)
|
509
|
+
puts [kind, type, value].inspect
|
480
510
|
end
|
481
511
|
|
482
|
-
def print_to_hash(
|
483
|
-
puts [
|
512
|
+
def print_to_hash(kind:, type:, value:)
|
513
|
+
puts [kind, type, value].inspect
|
484
514
|
end
|
485
515
|
|
486
516
|
print_to_ary(*success_data) # [:success, :ok, 1]
|
@@ -570,7 +600,7 @@ module Divide
|
|
570
600
|
|
571
601
|
def call(arg1, arg2)
|
572
602
|
validate_numbers(arg1, arg2)
|
573
|
-
.and_then { |numbers|
|
603
|
+
.and_then { |numbers| validate_nonzero(numbers) }
|
574
604
|
.and_then { |numbers| divide(numbers) }
|
575
605
|
end
|
576
606
|
|
@@ -583,8 +613,8 @@ module Divide
|
|
583
613
|
BCDD::Result::Success(:ok, [arg1, arg2])
|
584
614
|
end
|
585
615
|
|
586
|
-
def
|
587
|
-
return BCDD::Result::Success(:ok, numbers)
|
616
|
+
def validate_nonzero(numbers)
|
617
|
+
return BCDD::Result::Success(:ok, numbers) if numbers.last.nonzero?
|
588
618
|
|
589
619
|
BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero')
|
590
620
|
end
|
@@ -615,9 +645,11 @@ Divide.call(2, 2)
|
|
615
645
|
|
616
646
|
#### `BCDD::Result.mixin`
|
617
647
|
|
618
|
-
This method generates a module that can
|
648
|
+
This method generates a module that any object can include or extend. It adds two methods to the target object: `Success()` and `Failure()`.
|
649
|
+
|
650
|
+
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.
|
619
651
|
|
620
|
-
|
652
|
+
Because the result has a subject, the `#and_then` method can call methods from it.
|
621
653
|
|
622
654
|
##### Class example (Instance Methods)
|
623
655
|
|
@@ -634,7 +666,7 @@ class Divide
|
|
634
666
|
|
635
667
|
def call
|
636
668
|
validate_numbers
|
637
|
-
.and_then(:
|
669
|
+
.and_then(:validate_nonzero)
|
638
670
|
.and_then(:divide)
|
639
671
|
end
|
640
672
|
|
@@ -649,7 +681,7 @@ class Divide
|
|
649
681
|
Success(:ok, [arg1, arg2])
|
650
682
|
end
|
651
683
|
|
652
|
-
def
|
684
|
+
def validate_nonzero(numbers)
|
653
685
|
return Success(:ok, numbers) unless numbers.last.zero?
|
654
686
|
|
655
687
|
Failure(:division_by_zero, 'arg2 must not be zero')
|
@@ -675,7 +707,7 @@ module Divide
|
|
675
707
|
|
676
708
|
def call(arg1, arg2)
|
677
709
|
validate_numbers(arg1, arg2)
|
678
|
-
.and_then(:
|
710
|
+
.and_then(:validate_nonzero)
|
679
711
|
.and_then(:divide)
|
680
712
|
end
|
681
713
|
|
@@ -688,7 +720,7 @@ module Divide
|
|
688
720
|
Success(:ok, [arg1, arg2])
|
689
721
|
end
|
690
722
|
|
691
|
-
def
|
723
|
+
def validate_nonzero(numbers)
|
692
724
|
return Success(:ok, numbers) unless numbers.last.zero?
|
693
725
|
|
694
726
|
Failure(:division_by_zero, 'arg2 must not be zero')
|
@@ -717,7 +749,7 @@ If you try to use `BCDD::Result::Subject()`/`BCDD::Result::Failure()`, or result
|
|
717
749
|
**Note:** You can still use the block syntax, but all the results must be produced by the subject's `Success()` and `Failure()` methods.
|
718
750
|
|
719
751
|
```ruby
|
720
|
-
module
|
752
|
+
module ValidateNonzero
|
721
753
|
extend self, BCDD::Result.mixin
|
722
754
|
|
723
755
|
def call(numbers)
|
@@ -727,40 +759,83 @@ module ValidateNonZero
|
|
727
759
|
end
|
728
760
|
end
|
729
761
|
|
730
|
-
|
731
|
-
|
762
|
+
module Divide
|
763
|
+
extend self, BCDD::Result.mixin
|
732
764
|
|
733
|
-
|
765
|
+
def call(arg1, arg2)
|
766
|
+
validate_numbers(arg1, arg2)
|
767
|
+
.and_then(:validate_nonzero)
|
768
|
+
.and_then(:divide)
|
769
|
+
end
|
734
770
|
|
735
|
-
|
736
|
-
|
737
|
-
|
771
|
+
private
|
772
|
+
|
773
|
+
def validate_numbers(arg1, arg2)
|
774
|
+
arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
|
775
|
+
arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
|
776
|
+
|
777
|
+
Success(:ok, [arg1, arg2])
|
738
778
|
end
|
739
779
|
|
740
|
-
def
|
741
|
-
|
742
|
-
|
780
|
+
def validate_nonzero(numbers)
|
781
|
+
ValidateNonzero.call(numbers) # This will raise an error
|
782
|
+
end
|
783
|
+
|
784
|
+
def divide((number1, number2))
|
785
|
+
Success(:division_completed, number1 / number2)
|
786
|
+
end
|
787
|
+
end
|
788
|
+
```
|
789
|
+
|
790
|
+
Look at the error produced by the code above:
|
791
|
+
|
792
|
+
```ruby
|
793
|
+
Divide.call(2, 0)
|
794
|
+
|
795
|
+
# You cannot call #and_then and return a result that does not belong to the subject! (BCDD::Result::Error::InvalidResultSubject)
|
796
|
+
# Expected subject: Divide
|
797
|
+
# Given subject: ValidateNonzero
|
798
|
+
# Given result: #<BCDD::Result::Failure type=:division_by_zero value="arg2 must not be zero">
|
799
|
+
```
|
800
|
+
|
801
|
+
In order to fix this, you must handle the result produced by `ValidateNonzero.call()` and return a result that belongs to the subject.
|
802
|
+
|
803
|
+
```ruby
|
804
|
+
module ValidateNonzero
|
805
|
+
extend self, BCDD::Result.mixin
|
806
|
+
|
807
|
+
def call(numbers)
|
808
|
+
return Success(:ok, numbers) unless numbers.last.zero?
|
809
|
+
|
810
|
+
Failure(:division_by_zero, 'arg2 must not be zero')
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
module Divide
|
815
|
+
extend self, BCDD::Result.mixin
|
816
|
+
|
817
|
+
def call(arg1, arg2)
|
818
|
+
validate_numbers(arg1, arg2)
|
819
|
+
.and_then(:validate_nonzero)
|
743
820
|
.and_then(:divide)
|
744
821
|
end
|
745
822
|
|
746
823
|
private
|
747
824
|
|
748
|
-
def validate_numbers
|
749
|
-
arg1.is_a?(::Numeric) or return
|
825
|
+
def validate_numbers(arg1, arg2)
|
826
|
+
arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
|
750
827
|
arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
|
751
828
|
|
752
|
-
|
829
|
+
Success(:ok, [arg1, arg2])
|
753
830
|
end
|
754
831
|
|
755
|
-
def
|
756
|
-
ValidateNonZero.call(numbers) # This will raise an error
|
757
|
-
|
758
|
-
# This would work:
|
832
|
+
def validate_nonzero(numbers)
|
759
833
|
# In this case we are handling the other subject result and returning our own
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
834
|
+
ValidateNonzero.call(numbers).handle do |on|
|
835
|
+
on.success { |numbers| Success(:ok, numbers) }
|
836
|
+
|
837
|
+
on.failure { |err| Failure(:division_by_zero, err) }
|
838
|
+
end
|
764
839
|
end
|
765
840
|
|
766
841
|
def divide((number1, number2))
|
@@ -769,11 +844,20 @@ class Divide
|
|
769
844
|
end
|
770
845
|
```
|
771
846
|
|
847
|
+
Look at the output of the code above:
|
848
|
+
|
849
|
+
```ruby
|
850
|
+
Divide.call(2, 0)
|
851
|
+
|
852
|
+
#<BCDD::Result::Failure type=:division_by_zero value="arg2 must not be zero">
|
853
|
+
```
|
854
|
+
|
772
855
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
773
856
|
|
774
857
|
##### Dependency Injection
|
775
858
|
|
776
|
-
The `BCDD::Result#and_then` accepts a second argument that will be used to share a value with the subject's method.
|
859
|
+
The `BCDD::Result#and_then` accepts a second argument that will be used to share a value with the subject's method.
|
860
|
+
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.
|
777
861
|
|
778
862
|
```ruby
|
779
863
|
require 'logger'
|
@@ -783,7 +867,7 @@ module Divide
|
|
783
867
|
|
784
868
|
def call(arg1, arg2, logger: ::Logger.new(STDOUT))
|
785
869
|
validate_numbers(arg1, arg2)
|
786
|
-
.and_then(:
|
870
|
+
.and_then(:validate_nonzero, logger)
|
787
871
|
.and_then(:divide, logger)
|
788
872
|
end
|
789
873
|
|
@@ -796,7 +880,7 @@ module Divide
|
|
796
880
|
Success(:ok, [arg1, arg2])
|
797
881
|
end
|
798
882
|
|
799
|
-
def
|
883
|
+
def validate_nonzero(numbers, logger)
|
800
884
|
if numbers.last.zero?
|
801
885
|
logger.error('arg2 must not be zero')
|
802
886
|
|
@@ -830,19 +914,21 @@ Divide.call(4, 2, logger: Logger.new(IO::NULL))
|
|
830
914
|
|
831
915
|
##### Add-ons
|
832
916
|
|
833
|
-
The `BCDD::Result.mixin` also accepts the `
|
917
|
+
The `BCDD::Result.mixin` also accepts the `config:` argument. It is a hash that will be used to define custom behaviors for the mixin.
|
834
918
|
|
835
|
-
**
|
919
|
+
**continue**
|
836
920
|
|
837
|
-
This addon will create the `Continue(value)` method
|
921
|
+
This addon will create the `Continue(value)` method and change the `Success()` behavior to halt the step chain.
|
922
|
+
|
923
|
+
So, if you want to advance to the next step, you must use `Continue(value)` instead of `Success(type, value)`. Otherwise, the step chain will be halted.
|
838
924
|
|
839
925
|
```ruby
|
840
926
|
module Divide
|
841
|
-
extend self, BCDD::Result.mixin(
|
927
|
+
extend self, BCDD::Result.mixin(config: { addon: { continue: true } })
|
842
928
|
|
843
929
|
def call(arg1, arg2)
|
844
930
|
validate_numbers(arg1, arg2)
|
845
|
-
.and_then(:
|
931
|
+
.and_then(:validate_nonzero)
|
846
932
|
.and_then(:divide)
|
847
933
|
end
|
848
934
|
|
@@ -855,7 +941,7 @@ module Divide
|
|
855
941
|
Continue([arg1, arg2])
|
856
942
|
end
|
857
943
|
|
858
|
-
def
|
944
|
+
def validate_nonzero(numbers)
|
859
945
|
return Continue(numbers) unless numbers.last.zero?
|
860
946
|
|
861
947
|
Failure(:division_by_zero, 'arg2 must not be zero')
|
@@ -904,11 +990,11 @@ Look what happens if you try to create a result without one of the expected type
|
|
904
990
|
```ruby
|
905
991
|
Divide::Result::Success(:ok)
|
906
992
|
# type :ok is not allowed. Allowed types: :numbers, :division_completed
|
907
|
-
# (BCDD::Result::
|
993
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
908
994
|
|
909
995
|
Divide::Result::Failure(:err)
|
910
996
|
# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
|
911
|
-
# (BCDD::Result::
|
997
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
912
998
|
```
|
913
999
|
|
914
1000
|
The _**mixin mode**_ is similar to `BCDD::Result::Mixin`, but it also defines the expectations for the result's types and values.
|
@@ -922,7 +1008,7 @@ class Divide
|
|
922
1008
|
|
923
1009
|
def call(arg1, arg2)
|
924
1010
|
validate_numbers(arg1, arg2)
|
925
|
-
.and_then(:
|
1011
|
+
.and_then(:validate_nonzero)
|
926
1012
|
.and_then(:divide)
|
927
1013
|
end
|
928
1014
|
|
@@ -935,7 +1021,7 @@ class Divide
|
|
935
1021
|
Success(:numbers, [arg1, arg2])
|
936
1022
|
end
|
937
1023
|
|
938
|
-
def
|
1024
|
+
def validate_nonzero(numbers)
|
939
1025
|
return Success(:numbers, numbers) unless numbers.last.zero?
|
940
1026
|
|
941
1027
|
Failure(:division_by_zero, 'arg2 must not be zero')
|
@@ -947,10 +1033,10 @@ class Divide
|
|
947
1033
|
end
|
948
1034
|
```
|
949
1035
|
|
950
|
-
This mode also defines an `
|
1036
|
+
This mode also defines an `Result` constant to be used inside and outside the module.
|
951
1037
|
|
952
1038
|
> **PROTIP:**
|
953
|
-
> You can use the `
|
1039
|
+
> 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.
|
954
1040
|
|
955
1041
|
Now that you know the two modes, let's understand how expectations can be beneficial and powerful for defining contracts.
|
956
1042
|
|
@@ -975,7 +1061,7 @@ result.success?(:division_completed) # true
|
|
975
1061
|
|
976
1062
|
result.success?(:ok)
|
977
1063
|
# type :ok is not allowed. Allowed types: :numbers, :division_completed
|
978
|
-
# (BCDD::Result::
|
1064
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
979
1065
|
```
|
980
1066
|
|
981
1067
|
**Failure example:**
|
@@ -989,7 +1075,7 @@ result.failure?(:division_by_zero) # false
|
|
989
1075
|
|
990
1076
|
result.failure?(:err)
|
991
1077
|
# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
|
992
|
-
# (BCDD::Result::
|
1078
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
993
1079
|
```
|
994
1080
|
|
995
1081
|
*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
|
@@ -1011,7 +1097,7 @@ result
|
|
1011
1097
|
|
1012
1098
|
result.on(:number) { |_| :this_type_does_not_exist }
|
1013
1099
|
# type :number is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero
|
1014
|
-
# (BCDD::Result::
|
1100
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1015
1101
|
```
|
1016
1102
|
|
1017
1103
|
*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
|
@@ -1037,11 +1123,11 @@ result
|
|
1037
1123
|
|
1038
1124
|
result.on_success(:ok) { |_| :this_type_does_not_exist }
|
1039
1125
|
# type :ok is not allowed. Allowed types: :numbers, :division_completed
|
1040
|
-
# (BCDD::Result::
|
1126
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1041
1127
|
|
1042
1128
|
result.on_failure(:err) { |_| :this_type_does_not_exist }
|
1043
1129
|
# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
|
1044
|
-
# (BCDD::Result::
|
1130
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1045
1131
|
```
|
1046
1132
|
|
1047
1133
|
*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
|
@@ -1058,17 +1144,17 @@ result = Divide.call(10, 2)
|
|
1058
1144
|
result.handle do |on|
|
1059
1145
|
on.type(:ok) { |_| :this_type_does_not_exist }
|
1060
1146
|
end
|
1061
|
-
# type :ok is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero (BCDD::Result::
|
1147
|
+
# type :ok is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
|
1062
1148
|
|
1063
1149
|
result.handle do |on|
|
1064
1150
|
on.success(:ok) { |_| :this_type_does_not_exist }
|
1065
1151
|
end
|
1066
|
-
# type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::
|
1152
|
+
# type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
|
1067
1153
|
|
1068
1154
|
result.handle do |on|
|
1069
1155
|
on.failure(:err) { |_| :this_type_does_not_exist }
|
1070
1156
|
end
|
1071
|
-
# type :err is not allowed. Allowed types: :
|
1157
|
+
# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
|
1072
1158
|
```
|
1073
1159
|
|
1074
1160
|
*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
|
@@ -1099,11 +1185,11 @@ end
|
|
1099
1185
|
|
1100
1186
|
Divide.call('4', 2)
|
1101
1187
|
# type :invalid_arg is not allowed. Allowed types: :err
|
1102
|
-
# (BCDD::Result::
|
1188
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1103
1189
|
|
1104
1190
|
Divide.call(4, 2)
|
1105
1191
|
# type :division_completed is not allowed. Allowed types: :ok
|
1106
|
-
# (BCDD::Result::
|
1192
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1107
1193
|
```
|
1108
1194
|
|
1109
1195
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
@@ -1126,11 +1212,11 @@ end
|
|
1126
1212
|
|
1127
1213
|
Divide.call('4', 2)
|
1128
1214
|
# type :invalid_arg is not allowed. Allowed types: :err
|
1129
|
-
# (BCDD::Result::
|
1215
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1130
1216
|
|
1131
1217
|
Divide.call(4, 2)
|
1132
1218
|
# type :division_completed is not allowed. Allowed types: :ok
|
1133
|
-
# (BCDD::Result::
|
1219
|
+
# (BCDD::Result::Contract::Error::UnexpectedType)
|
1134
1220
|
```
|
1135
1221
|
|
1136
1222
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
@@ -1201,26 +1287,26 @@ The value validation will only be performed through the methods `Success()` and
|
|
1201
1287
|
|
1202
1288
|
```ruby
|
1203
1289
|
Divide::Result::Success(:ok)
|
1204
|
-
# type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::
|
1290
|
+
# type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
|
1205
1291
|
|
1206
1292
|
Divide::Result::Success(:numbers, [1])
|
1207
|
-
# value [1] is not allowed for :numbers type (BCDD::Result::
|
1293
|
+
# value [1] is not allowed for :numbers type (BCDD::Result::Contract::Error::UnexpectedValue)
|
1208
1294
|
|
1209
1295
|
Divide::Result::Success(:division_completed, '2')
|
1210
|
-
# value "2" is not allowed for :division_completed type (BCDD::Result::
|
1296
|
+
# value "2" is not allowed for :division_completed type (BCDD::Result::Contract::Error::UnexpectedValue)
|
1211
1297
|
```
|
1212
1298
|
|
1213
1299
|
##### Failure()
|
1214
1300
|
|
1215
1301
|
```ruby
|
1216
1302
|
Divide::Result::Failure(:err)
|
1217
|
-
# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::
|
1303
|
+
# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
|
1218
1304
|
|
1219
1305
|
Divide::Result::Failure(:invalid_arg, :arg1_must_be_numeric)
|
1220
|
-
# value :arg1_must_be_numeric is not allowed for :invalid_arg type (BCDD::Result::
|
1306
|
+
# value :arg1_must_be_numeric is not allowed for :invalid_arg type (BCDD::Result::Contract::Error::UnexpectedValue)
|
1221
1307
|
|
1222
1308
|
Divide::Result::Failure(:division_by_zero, msg: 'arg2 must not be zero')
|
1223
|
-
# value {:msg=>"arg2 must not be zero"} is not allowed for :division_by_zero type (BCDD::Result::
|
1309
|
+
# value {:msg=>"arg2 must not be zero"} is not allowed for :division_by_zero type (BCDD::Result::Contract::Error::UnexpectedValue)
|
1224
1310
|
```
|
1225
1311
|
|
1226
1312
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
@@ -1231,21 +1317,14 @@ The value checking has support for handling pattern-matching errors, and the cle
|
|
1231
1317
|
|
1232
1318
|
How does this operator work? They raise an error when the pattern does not match but returns nil when it matches.
|
1233
1319
|
|
1234
|
-
Because of this, you will need to enable `nil` as a valid value checking. You can do it
|
1235
|
-
|
1236
|
-
**Attention:**
|
1237
|
-
|
1238
|
-
If you decide to enable this, you will do it at the beginning of your code or in an initializer. And remember, this will affect all kinds of result expectations (`BCDD::Result::Expectations` and `BCDD::Result::Context::Expectations`). So, it is recommended to use it only when you are using pattern matching for **ALL** the result's value validations.
|
1320
|
+
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.
|
1239
1321
|
|
1240
1322
|
```ruby
|
1241
|
-
#
|
1242
|
-
# Put this line in an initializer or at the beginning of your code.
|
1243
|
-
# It is required if you decide to use pattern matching to validate all of your result's values.
|
1244
|
-
#
|
1245
|
-
BCDD::Result::Contract.nil_as_valid_value_checking!
|
1246
|
-
|
1247
1323
|
module Divide
|
1248
1324
|
extend BCDD::Result::Expectations.mixin(
|
1325
|
+
config: {
|
1326
|
+
pattern_matching: { nil_as_valid_value_checking: true }
|
1327
|
+
},
|
1249
1328
|
success: {
|
1250
1329
|
division_completed: ->(value) { value => (Integer | Float) }
|
1251
1330
|
},
|
@@ -1263,30 +1342,32 @@ module Divide
|
|
1263
1342
|
end
|
1264
1343
|
|
1265
1344
|
Divide.call(10, 5)
|
1266
|
-
# value "
|
1345
|
+
# value "2" is not allowed for :division_completed type ("2": Float === "2" does not return true) (BCDD::Result::Contract::Error::UnexpectedValue)
|
1267
1346
|
```
|
1268
1347
|
|
1269
1348
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1270
1349
|
|
1271
1350
|
#### `BCDD::Result::Expectations.mixin` add-ons
|
1272
1351
|
|
1273
|
-
The `BCDD::Result::Expectations.mixin` also accepts the `
|
1352
|
+
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.
|
1274
1353
|
|
1275
1354
|
**Continue**
|
1276
1355
|
|
1277
|
-
It is similar to `BCDD::Result.mixin(
|
1356
|
+
It is similar to `BCDD::Result.mixin(config: { addon: { continue: true } })`. The key difference is that the expectations will ignore the `Continue(value)`.
|
1357
|
+
|
1358
|
+
Based on this, use the `Success()` to produce a terminal result and `Continue()` to produce a result that will be used in the next step.
|
1278
1359
|
|
1279
1360
|
```ruby
|
1280
1361
|
class Divide
|
1281
1362
|
include BCDD::Result::Expectations.mixin(
|
1282
|
-
|
1363
|
+
config: { addon: { continue: true } },
|
1283
1364
|
success: :division_completed,
|
1284
1365
|
failure: %i[invalid_arg division_by_zero]
|
1285
1366
|
)
|
1286
1367
|
|
1287
1368
|
def call(arg1, arg2)
|
1288
1369
|
validate_numbers(arg1, arg2)
|
1289
|
-
.and_then(:
|
1370
|
+
.and_then(:validate_nonzero)
|
1290
1371
|
.and_then(:divide)
|
1291
1372
|
end
|
1292
1373
|
|
@@ -1299,7 +1380,7 @@ class Divide
|
|
1299
1380
|
Continue([arg1, arg2])
|
1300
1381
|
end
|
1301
1382
|
|
1302
|
-
def
|
1383
|
+
def validate_nonzero(numbers)
|
1303
1384
|
return Continue(numbers) unless numbers.last.zero?
|
1304
1385
|
|
1305
1386
|
Failure(:division_by_zero, 'arg2 must not be zero')
|
@@ -1310,7 +1391,7 @@ class Divide
|
|
1310
1391
|
end
|
1311
1392
|
end
|
1312
1393
|
|
1313
|
-
result = Divide.new.call(4,2)
|
1394
|
+
result = Divide.new.call(4, 2)
|
1314
1395
|
# => #<BCDD::Result::Success type=:division_completed value=2>
|
1315
1396
|
|
1316
1397
|
# The example below shows an error because the :ok type is not allowed.
|
@@ -1318,7 +1399,7 @@ result = Divide.new.call(4,2)
|
|
1318
1399
|
# This is because the :continued type is ignored by the expectations.
|
1319
1400
|
#
|
1320
1401
|
result.success?(:ok)
|
1321
|
-
# type :ok is not allowed. Allowed types: :division_completed (BCDD::Result::
|
1402
|
+
# type :ok is not allowed. Allowed types: :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
|
1322
1403
|
```
|
1323
1404
|
|
1324
1405
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
@@ -1359,6 +1440,22 @@ BCDD::Result::Context::Success(:ok, **{ message: 'hashes can be converted to key
|
|
1359
1440
|
|
1360
1441
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1361
1442
|
|
1443
|
+
#### Constant aliases
|
1444
|
+
|
1445
|
+
You can configure `Context` or `BCDD::Context` as an alias for `BCDD::Result::Context`. This is helpful to define a standard way to avoid the full constant name/path in your code.
|
1446
|
+
|
1447
|
+
```ruby
|
1448
|
+
BCDD::Result.configuration do |config|
|
1449
|
+
config.context_alias.enable!('BCDD::Context')
|
1450
|
+
|
1451
|
+
# or
|
1452
|
+
|
1453
|
+
config.context_alias.enable!('Context')
|
1454
|
+
end
|
1455
|
+
```
|
1456
|
+
|
1457
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1458
|
+
|
1362
1459
|
#### `BCDD::Result::Context.mixin`
|
1363
1460
|
|
1364
1461
|
As in the `BCDD::Result`, you can use the `BCDD::Result::Context.mixin` to add the `Success()` and `Failure()` methods to your classes/modules.
|
@@ -1368,14 +1465,14 @@ Let's see this feature and the data accumulation in action:
|
|
1368
1465
|
##### Class example (Instance Methods)
|
1369
1466
|
|
1370
1467
|
```ruby
|
1371
|
-
|
1372
|
-
require 'logger'
|
1468
|
+
require 'logger'
|
1373
1469
|
|
1470
|
+
class Divide
|
1374
1471
|
include BCDD::Result::Context.mixin
|
1375
1472
|
|
1376
1473
|
def call(arg1, arg2, logger: ::Logger.new(STDOUT))
|
1377
1474
|
validate_numbers(arg1, arg2)
|
1378
|
-
.and_then(:
|
1475
|
+
.and_then(:validate_nonzero)
|
1379
1476
|
.and_then(:divide, logger: logger)
|
1380
1477
|
end
|
1381
1478
|
|
@@ -1388,7 +1485,7 @@ class Divide
|
|
1388
1485
|
Success(:ok, number1: arg1, number2: arg2)
|
1389
1486
|
end
|
1390
1487
|
|
1391
|
-
def
|
1488
|
+
def validate_nonzero(number2:, **)
|
1392
1489
|
return Success(:ok) if number2.nonzero?
|
1393
1490
|
|
1394
1491
|
Failure(:err, message: 'arg2 must not be zero')
|
@@ -1434,8 +1531,9 @@ class Divide
|
|
1434
1531
|
|
1435
1532
|
def call(arg1, arg2)
|
1436
1533
|
validate_numbers(arg1, arg2)
|
1437
|
-
.and_then(:
|
1534
|
+
.and_then(:validate_nonzero)
|
1438
1535
|
.and_then(:divide)
|
1536
|
+
.and_expose(:division_completed, [:number])
|
1439
1537
|
end
|
1440
1538
|
|
1441
1539
|
private
|
@@ -1447,7 +1545,7 @@ class Divide
|
|
1447
1545
|
Success(:ok, number1: arg1, number2: arg2)
|
1448
1546
|
end
|
1449
1547
|
|
1450
|
-
def
|
1548
|
+
def validate_nonzero(number2:, **)
|
1451
1549
|
return Success(:ok) if number2.nonzero?
|
1452
1550
|
|
1453
1551
|
Failure(:err, message: 'arg2 must not be zero')
|
@@ -1483,7 +1581,7 @@ module Divide
|
|
1483
1581
|
|
1484
1582
|
def call(arg1, arg2)
|
1485
1583
|
validate_numbers(arg1, arg2)
|
1486
|
-
.and_then(:
|
1584
|
+
.and_then(:validate_nonzero)
|
1487
1585
|
.and_then(:divide)
|
1488
1586
|
.and_expose(:division_completed, [:number])
|
1489
1587
|
end
|
@@ -1497,7 +1595,7 @@ module Divide
|
|
1497
1595
|
Success(:ok, number1: arg1, number2: arg2)
|
1498
1596
|
end
|
1499
1597
|
|
1500
|
-
def
|
1598
|
+
def validate_nonzero(number2:, **)
|
1501
1599
|
return Success(:ok) if number2.nonzero?
|
1502
1600
|
|
1503
1601
|
Failure(:err, message: 'arg2 must not be zero')
|
@@ -1530,14 +1628,11 @@ The `BCDD::Result::Context::Expectations` is a `BCDD::Result::Expectations` with
|
|
1530
1628
|
This is an example using the mixin mode, but the standalone mode is also supported.
|
1531
1629
|
|
1532
1630
|
```ruby
|
1533
|
-
#
|
1534
|
-
# Put this line in an initializer or at the beginning of your code.
|
1535
|
-
# It is required if you decide to use pattern matching to validate all of your result's values.
|
1536
|
-
#
|
1537
|
-
BCDD::Result::Contract.nil_as_valid_value_checking!
|
1538
|
-
|
1539
1631
|
class Divide
|
1540
1632
|
include BCDD::Result::Context::Expectations.mixin(
|
1633
|
+
config: {
|
1634
|
+
pattern_matching: { nil_as_valid_value_checking: true }
|
1635
|
+
},
|
1541
1636
|
success: {
|
1542
1637
|
division_completed: ->(value) { value => { number: Numeric } }
|
1543
1638
|
},
|
@@ -1553,7 +1648,7 @@ class Divide
|
|
1553
1648
|
|
1554
1649
|
arg2.zero? and return Failure(:division_by_zero, message: 'arg2 must not be zero')
|
1555
1650
|
|
1556
|
-
Success(:division_completed, number: arg1 / arg2)
|
1651
|
+
Success(:division_completed, number: (arg1 / arg2))
|
1557
1652
|
end
|
1558
1653
|
end
|
1559
1654
|
|
@@ -1577,26 +1672,25 @@ Divide::Result::Success(:division_completed, number: '2')
|
|
1577
1672
|
|
1578
1673
|
#### Mixin add-ons
|
1579
1674
|
|
1580
|
-
The `BCDD::Result::Context.mixin` and `BCDD::Result::Context::Expectations.mixin` also accepts the `
|
1675
|
+
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.
|
1581
1676
|
|
1582
1677
|
**Continue**
|
1583
1678
|
|
1584
|
-
The `BCDD::Result::Context.mixin(
|
1679
|
+
The `BCDD::Result::Context.mixin(config: { addon: { continue: true } })` or `BCDD::Result::Context::Expectations.mixin(config: { addon: { continue: true } })` creates the `Continue(value)` method and change the `Success()` behavior to halt the step chain.
|
1680
|
+
|
1681
|
+
So, if you want to advance to the next step, you must use `Continue(**value)` instead of `Success(type, **value)`. Otherwise, the step chain will be halted.
|
1585
1682
|
|
1586
1683
|
Let's use a mix of `BCDD::Result::Context` features to see in action with this add-on:
|
1587
1684
|
|
1588
1685
|
```ruby
|
1589
|
-
#
|
1590
|
-
# Put this line in an initializer or at the beginning of your code.
|
1591
|
-
# It is required if you decide to use pattern matching to validate all of your result's values.
|
1592
|
-
#
|
1593
|
-
BCDD::Result::Contract.nil_as_valid_value_checking!
|
1594
|
-
|
1595
1686
|
module Divide
|
1596
1687
|
require 'logger'
|
1597
1688
|
|
1598
1689
|
extend self, BCDD::Result::Context::Expectations.mixin(
|
1599
|
-
|
1690
|
+
config: {
|
1691
|
+
addon: { continue: true },
|
1692
|
+
pattern_matching: { nil_as_valid_value_checking: true }
|
1693
|
+
},
|
1600
1694
|
success: {
|
1601
1695
|
division_completed: ->(value) { value => { number: Numeric } }
|
1602
1696
|
},
|
@@ -1608,7 +1702,7 @@ module Divide
|
|
1608
1702
|
|
1609
1703
|
def call(arg1, arg2, logger: ::Logger.new(STDOUT))
|
1610
1704
|
validate_numbers(arg1, arg2)
|
1611
|
-
.and_then(:
|
1705
|
+
.and_then(:validate_nonzero)
|
1612
1706
|
.and_then(:divide, logger: logger)
|
1613
1707
|
.and_expose(:division_completed, [:number])
|
1614
1708
|
end
|
@@ -1622,7 +1716,7 @@ module Divide
|
|
1622
1716
|
Continue(number1: arg1, number2: arg2)
|
1623
1717
|
end
|
1624
1718
|
|
1625
|
-
def
|
1719
|
+
def validate_nonzero(number2:, **)
|
1626
1720
|
return Continue() if number2.nonzero?
|
1627
1721
|
|
1628
1722
|
Failure(:division_by_zero, message: 'arg2 must not be zero')
|
@@ -1651,8 +1745,122 @@ Divide.call(14, 0)
|
|
1651
1745
|
#<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
|
1652
1746
|
```
|
1653
1747
|
|
1748
|
+
### `BCDD::Result.configuration`
|
1749
|
+
|
1750
|
+
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.
|
1751
|
+
|
1752
|
+
```ruby
|
1753
|
+
BCDD::Result.configuration do |config|
|
1754
|
+
config.addon.enable!(:continue)
|
1755
|
+
|
1756
|
+
config.constant_alias.enable!('Result', 'BCDD::Context')
|
1757
|
+
|
1758
|
+
config.pattern_matching.disable!(:nil_as_valid_value_checking)
|
1759
|
+
|
1760
|
+
# config.feature.disable!(:expectations) if ::Rails.env.production?
|
1761
|
+
end
|
1762
|
+
```
|
1763
|
+
|
1764
|
+
Use `disable!` to disable a feature and `enable!` to enable it.
|
1765
|
+
|
1766
|
+
Let's see what each configuration in the example above does:
|
1767
|
+
|
1768
|
+
#### `config.addon.enable!(:continue)`
|
1769
|
+
|
1770
|
+
This configuration enables the `Continue()` method for `BCDD::Result`, `BCDD::Result::Context`, `BCDD::Result::Expectation`, and `BCDD::Result::Context::Expectation`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).
|
1771
|
+
|
1772
|
+
#### `config.constant_alias.enable!('Result', 'BCDD::Context')`
|
1773
|
+
|
1774
|
+
This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::Result::Context`.
|
1775
|
+
|
1776
|
+
Link to documentations:
|
1777
|
+
- [Result alias](#bcddresult-versus-result)
|
1778
|
+
- [Context aliases](#constant-aliases)
|
1779
|
+
|
1780
|
+
#### `config.pattern_matching.disable!(:nil_as_valid_value_checking)`
|
1781
|
+
|
1782
|
+
This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::Result::Context`. Link to [documentation](#pattern-matching-support).
|
1783
|
+
|
1654
1784
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1655
1785
|
|
1786
|
+
#### `config.feature.disable!(:expectations)`
|
1787
|
+
|
1788
|
+
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.
|
1789
|
+
|
1790
|
+
PS: I'm using `::Rails.env.production?` to check the environment, but you can use any logic you want.
|
1791
|
+
|
1792
|
+
### `BCDD::Result.config`
|
1793
|
+
|
1794
|
+
The `BCDD::Result.config` allows you to access the current configuration. It is useful when you want to check the current configuration.
|
1795
|
+
|
1796
|
+
**BCDD::Result.config.addon**
|
1797
|
+
|
1798
|
+
```ruby
|
1799
|
+
BCDD::Result.config.addon.enabled?(:continue)
|
1800
|
+
|
1801
|
+
BCDD::Result.config.addon.options
|
1802
|
+
# {
|
1803
|
+
# :continue=>{
|
1804
|
+
# :enabled=>false,
|
1805
|
+
# :affects=>[
|
1806
|
+
# "BCDD::Result",
|
1807
|
+
# "BCDD::Result::Context",
|
1808
|
+
# "BCDD::Result::Expectations",
|
1809
|
+
# "BCDD::Result::Context::Expectations"
|
1810
|
+
# ]
|
1811
|
+
# }
|
1812
|
+
# }
|
1813
|
+
```
|
1814
|
+
|
1815
|
+
**BCDD::Result.config.constant_alias**
|
1816
|
+
|
1817
|
+
```ruby
|
1818
|
+
BCDD::Result.config.constant_alias.enabled?('Result')
|
1819
|
+
BCDD::Result.config.constant_alias.enabled?('Context')
|
1820
|
+
BCDD::Result.config.constant_alias.enabled?('BCDD::Context')
|
1821
|
+
|
1822
|
+
BCDD::Result.config.constant_alias.options
|
1823
|
+
# {
|
1824
|
+
# "Result"=>{:enabled=>false, :affects=>["Object"]},
|
1825
|
+
# "Context"=>{:enabled=>false, :affects=>["Object"]},
|
1826
|
+
# "BCDD::Context"=>{:enabled=>false, :affects=>["BCDD"]}
|
1827
|
+
# }
|
1828
|
+
```
|
1829
|
+
|
1830
|
+
**BCDD::Result.config.pattern_matching**
|
1831
|
+
|
1832
|
+
```ruby
|
1833
|
+
BCDD::Result.config.pattern_matching.enabled?(:nil_as_valid_value_checking)
|
1834
|
+
|
1835
|
+
BCDD::Result.config.pattern_matching.options
|
1836
|
+
# {
|
1837
|
+
# :nil_as_valid_value_checking=>{
|
1838
|
+
# :enabled=>false,
|
1839
|
+
# :affects=>[
|
1840
|
+
# "BCDD::Result::Expectations,
|
1841
|
+
# "BCDD::Result::Context::Expectations"
|
1842
|
+
# ]
|
1843
|
+
# }
|
1844
|
+
# }
|
1845
|
+
```
|
1846
|
+
|
1847
|
+
**BCDD::Result.config.feature**
|
1848
|
+
|
1849
|
+
```ruby
|
1850
|
+
BCDD::Result.config.feature.enabled?(:expectations)
|
1851
|
+
|
1852
|
+
BCDD::Result.config.feature.options
|
1853
|
+
# {
|
1854
|
+
# :expectations=>{
|
1855
|
+
# :enabled=>true,
|
1856
|
+
# :affects=>[
|
1857
|
+
# "BCDD::Result::Expectations,
|
1858
|
+
# "BCDD::Result::Context::Expectations"
|
1859
|
+
# ]
|
1860
|
+
# }
|
1861
|
+
# }
|
1862
|
+
```
|
1863
|
+
|
1656
1864
|
## About
|
1657
1865
|
|
1658
1866
|
[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.
|