bcdd-result 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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)
@@ -69,6 +69,12 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
69
69
  - [Module example (Singleton Methods)](#module-example-singleton-methods-1)
70
70
  - [`BCDD::Result::Context::Expectations`](#bcddresultcontextexpectations)
71
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)
72
78
  - [About](#about)
73
79
  - [Development](#development)
74
80
  - [Contributing](#contributing)
@@ -84,7 +90,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
84
90
  Add this line to your application's Gemfile:
85
91
 
86
92
  ```ruby
87
- gem 'bcdd-result', require: 'bcdd/result'
93
+ gem 'bcdd-result'
88
94
  ```
89
95
 
90
96
  And then execute:
@@ -95,6 +101,10 @@ If bundler is not being used to manage dependencies, install the gem by executin
95
101
 
96
102
  $ gem install bcdd-result
97
103
 
104
+ And require it in your code:
105
+
106
+ require 'bcdd/result'
107
+
98
108
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
99
109
 
100
110
  ## Usage
@@ -119,17 +129,27 @@ BCDD::Result::Failure(:err) #
119
129
 
120
130
  #### `BCDD::Result` *versus* `Result`
121
131
 
122
- 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.
123
133
 
124
- 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.
125
135
 
126
136
  ```ruby
127
- require 'result'
137
+ BCDD::Result.configuration do |config|
138
+ config.constant_alias.enable!('Result')
139
+ end
140
+ ```
141
+
142
+ So, instead of using `BCDD::Result` everywhere, you can use `Result` as an alias/shortcut.
128
143
 
144
+ ```ruby
129
145
  Result::Success(:ok) # <BCDD::Result::Success type=:ok value=nil>
146
+
147
+ Result::Failure(:err) # <BCDD::Result::Failure type=:err value=nil>
130
148
  ```
131
149
 
132
- 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.
133
153
 
134
154
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
135
155
 
@@ -169,12 +189,12 @@ result.value # nil
169
189
  ################
170
190
  # With a value #
171
191
  ################
172
- result = BCDD::Result::Failure(:err, my: 'value')
192
+ result = BCDD::Result::Failure(:err, 'my_value')
173
193
 
174
194
  result.success? # false
175
195
  result.failure? # true
176
196
  result.type # :err
177
- result.value # {:my => "value"}
197
+ result.value # "my_value"
178
198
 
179
199
  ###################
180
200
  # Without a value #
@@ -187,9 +207,11 @@ result.type # :no
187
207
  result.value # nil
188
208
  ```
189
209
 
210
+ In both cases, the `type` must be a symbol, and the `value` can be any kind of object.
211
+
190
212
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
191
213
 
192
- #### Receiving types in `result.success?` or `result.failure?`
214
+ #### Checking types with `result.success?` or `result.failure?`
193
215
 
194
216
  `BCDD::Result#success?` and `BCDD::Result#failure?` are methods that allow you to check if the result is a success or a failure.
195
217
 
@@ -198,9 +220,11 @@ You can also check the result type by passing an argument to it. For example, `r
198
220
  ```ruby
199
221
  result = BCDD::Result::Success(:ok)
200
222
 
201
- result.success? # true
202
- result.success?(:ok) # true
203
- result.success?(:okay) # false
223
+ result.success?(:ok)
224
+
225
+ # This is the same as:
226
+
227
+ result.success? && result.type == :ok
204
228
  ```
205
229
 
206
230
  The same is valid for `BCDD::Result#failure?`.
@@ -208,9 +232,11 @@ The same is valid for `BCDD::Result#failure?`.
208
232
  ```ruby
209
233
  result = BCDD::Result::Failure(:err)
210
234
 
211
- result.failure? # true
212
- result.failure?(:err) # true
213
- result.failure?(:error) # false
235
+ result.failure?(:err)
236
+
237
+ # This is the same as:
238
+
239
+ result.failure? && result.type == :err
214
240
  ```
215
241
 
216
242
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -218,7 +244,7 @@ result.failure?(:error) # false
218
244
  ### Result Hooks
219
245
 
220
246
  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 function that can divide two numbers.
247
+ To demonstrate their use, I will implement a method that can divide two numbers.
222
248
 
223
249
  ```ruby
224
250
  def divide(arg1, arg2)
@@ -282,8 +308,7 @@ result = divide(nil, 2)
282
308
 
283
309
  output =
284
310
  result
285
- .on_type(:invalid_arg) { |msg| puts msg }
286
- .on_type(:division_by_zero) { |msg| puts msg }
311
+ .on_type(:invalid_arg, :division_by_zero) { |msg| puts msg }
287
312
  .on_type(:division_completed) { |number| puts number }
288
313
 
289
314
  # The code above will print 'arg1 must be numeric' and return the result itself.
@@ -303,16 +328,18 @@ The `BCDD::Result#on_success` method is quite similar to the `BCDD::Result#on` h
303
328
  2. If the type declaration is not included, the method will execute the block for any successful result, regardless of its type.
304
329
 
305
330
  ```ruby
306
- # It executes the block and return itself.
331
+ # In both examples, it executes the block and returns the result itself.
307
332
 
308
333
  divide(4, 2).on_success { |number| puts number }
309
334
 
310
335
  divide(4, 2).on_success(:division_completed) { |number| puts number }
311
336
 
312
- # It doesn't execute the block, but return itself.
337
+ # It doesn't execute the block as the type is different.
313
338
 
314
339
  divide(4, 4).on_success(:ok) { |value| puts value }
315
340
 
341
+ # It doesn't execute the block, as the result is a success, but the hook expects a failure.
342
+
316
343
  divide(4, 4).on_failure { |error| puts error }
317
344
  ```
318
345
 
@@ -328,17 +355,19 @@ It is the opposite of `Result#on_success`:
328
355
  2. If the type declaration is not included, the method will execute the block for any failed result, regardless of its type.
329
356
 
330
357
  ```ruby
331
- # It executes the block and return itself.
358
+ # In both examples, it executes the block and returns the result itself.
332
359
 
333
360
  divide(nil, 2).on_failure { |error| puts error }
334
361
 
335
- 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 }
336
363
 
337
- # It doesn't execute the block, but return itself.
338
-
339
- divide(4, 0).on_success { |number| puts number }
364
+ # It doesn't execute the block as the type is different.
340
365
 
341
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 }
342
371
  ```
343
372
 
344
373
  *PS: The `divide()` implementation is [here](#result-hooks).*
@@ -570,7 +599,7 @@ module Divide
570
599
 
571
600
  def call(arg1, arg2)
572
601
  validate_numbers(arg1, arg2)
573
- .and_then { |numbers| validate_non_zero(numbers) }
602
+ .and_then { |numbers| validate_nonzero(numbers) }
574
603
  .and_then { |numbers| divide(numbers) }
575
604
  end
576
605
 
@@ -583,8 +612,8 @@ module Divide
583
612
  BCDD::Result::Success(:ok, [arg1, arg2])
584
613
  end
585
614
 
586
- def validate_non_zero(numbers)
587
- 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?
588
617
 
589
618
  BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero')
590
619
  end
@@ -615,9 +644,11 @@ Divide.call(2, 2)
615
644
 
616
645
  #### `BCDD::Result.mixin`
617
646
 
618
- This method generates a module that can be included or 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 former will utilize the target object (which has 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()`.
619
648
 
620
- As a result, you can utilize the `#and_then` method to invoke methods from the result's subject.
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.
650
+
651
+ Because the result has a subject, the `#and_then` method can call methods from it.
621
652
 
622
653
  ##### Class example (Instance Methods)
623
654
 
@@ -634,7 +665,7 @@ class Divide
634
665
 
635
666
  def call
636
667
  validate_numbers
637
- .and_then(:validate_non_zero)
668
+ .and_then(:validate_nonzero)
638
669
  .and_then(:divide)
639
670
  end
640
671
 
@@ -649,7 +680,7 @@ class Divide
649
680
  Success(:ok, [arg1, arg2])
650
681
  end
651
682
 
652
- def validate_non_zero(numbers)
683
+ def validate_nonzero(numbers)
653
684
  return Success(:ok, numbers) unless numbers.last.zero?
654
685
 
655
686
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -675,7 +706,7 @@ module Divide
675
706
 
676
707
  def call(arg1, arg2)
677
708
  validate_numbers(arg1, arg2)
678
- .and_then(:validate_non_zero)
709
+ .and_then(:validate_nonzero)
679
710
  .and_then(:divide)
680
711
  end
681
712
 
@@ -688,7 +719,7 @@ module Divide
688
719
  Success(:ok, [arg1, arg2])
689
720
  end
690
721
 
691
- def validate_non_zero(numbers)
722
+ def validate_nonzero(numbers)
692
723
  return Success(:ok, numbers) unless numbers.last.zero?
693
724
 
694
725
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -717,7 +748,7 @@ If you try to use `BCDD::Result::Subject()`/`BCDD::Result::Failure()`, or result
717
748
  **Note:** You can still use the block syntax, but all the results must be produced by the subject's `Success()` and `Failure()` methods.
718
749
 
719
750
  ```ruby
720
- module ValidateNonZero
751
+ module ValidateNonzero
721
752
  extend self, BCDD::Result.mixin
722
753
 
723
754
  def call(numbers)
@@ -727,40 +758,83 @@ module ValidateNonZero
727
758
  end
728
759
  end
729
760
 
730
- class Divide
731
- include BCDD::Result.mixin
761
+ module Divide
762
+ extend self, BCDD::Result.mixin
732
763
 
733
- attr_reader :arg1, :arg2
764
+ def call(arg1, arg2)
765
+ validate_numbers(arg1, arg2)
766
+ .and_then(:validate_nonzero)
767
+ .and_then(:divide)
768
+ end
734
769
 
735
- def initialize(arg1, arg2)
736
- @arg1 = arg1
737
- @arg2 = arg2
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])
738
777
  end
739
778
 
740
- def call
741
- validate_numbers
742
- .and_then(:validate_non_zero)
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
812
+
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)
743
819
  .and_then(:divide)
744
820
  end
745
821
 
746
822
  private
747
823
 
748
- def validate_numbers
749
- arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric') # This will raise an error
824
+ def validate_numbers(arg1, arg2)
825
+ arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
750
826
  arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
751
827
 
752
- BCDD::Result::Success(:ok, [arg1, arg2]) # This will raise an error
828
+ Success(:ok, [arg1, arg2])
753
829
  end
754
830
 
755
- def validate_non_zero(numbers)
756
- ValidateNonZero.call(numbers) # This will raise an error
757
-
758
- # This would work:
831
+ def validate_nonzero(numbers)
759
832
  # In this case we are handling the other subject result and returning our own
760
- # ValidateNonZero.call(numbers).handle do |on|
761
- # on.success { |numbers| Success(:ok, numbers) }
762
- # on.failure { |err| Failure(:division_by_zero, err) }
763
- # end
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
764
838
  end
765
839
 
766
840
  def divide((number1, number2))
@@ -769,11 +843,20 @@ class Divide
769
843
  end
770
844
  ```
771
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
+ ```
853
+
772
854
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
773
855
 
774
856
  ##### Dependency Injection
775
857
 
776
- 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.
777
860
 
778
861
  ```ruby
779
862
  require 'logger'
@@ -783,7 +866,7 @@ module Divide
783
866
 
784
867
  def call(arg1, arg2, logger: ::Logger.new(STDOUT))
785
868
  validate_numbers(arg1, arg2)
786
- .and_then(:validate_non_zero, logger)
869
+ .and_then(:validate_nonzero, logger)
787
870
  .and_then(:divide, logger)
788
871
  end
789
872
 
@@ -796,7 +879,7 @@ module Divide
796
879
  Success(:ok, [arg1, arg2])
797
880
  end
798
881
 
799
- def validate_non_zero(numbers, logger)
882
+ def validate_nonzero(numbers, logger)
800
883
  if numbers.last.zero?
801
884
  logger.error('arg2 must not be zero')
802
885
 
@@ -830,19 +913,19 @@ Divide.call(4, 2, logger: Logger.new(IO::NULL))
830
913
 
831
914
  ##### Add-ons
832
915
 
833
- 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.
834
917
 
835
- **Continue**
918
+ **continue**
836
919
 
837
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.
838
921
 
839
922
  ```ruby
840
923
  module Divide
841
- extend self, BCDD::Result.mixin(with: :Continue)
924
+ extend self, BCDD::Result.mixin(config: { addon: { continue: true } })
842
925
 
843
926
  def call(arg1, arg2)
844
927
  validate_numbers(arg1, arg2)
845
- .and_then(:validate_non_zero)
928
+ .and_then(:validate_nonzero)
846
929
  .and_then(:divide)
847
930
  end
848
931
 
@@ -855,7 +938,7 @@ module Divide
855
938
  Continue([arg1, arg2])
856
939
  end
857
940
 
858
- def validate_non_zero(numbers)
941
+ def validate_nonzero(numbers)
859
942
  return Continue(numbers) unless numbers.last.zero?
860
943
 
861
944
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -904,11 +987,11 @@ Look what happens if you try to create a result without one of the expected type
904
987
  ```ruby
905
988
  Divide::Result::Success(:ok)
906
989
  # type :ok is not allowed. Allowed types: :numbers, :division_completed
907
- # (BCDD::Result::Expectations::Error::UnexpectedType)
990
+ # (BCDD::Result::Contract::Error::UnexpectedType)
908
991
 
909
992
  Divide::Result::Failure(:err)
910
993
  # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
911
- # (BCDD::Result::Expectations::Error::UnexpectedType)
994
+ # (BCDD::Result::Contract::Error::UnexpectedType)
912
995
  ```
913
996
 
914
997
  The _**mixin mode**_ is similar to `BCDD::Result::Mixin`, but it also defines the expectations for the result's types and values.
@@ -922,7 +1005,7 @@ class Divide
922
1005
 
923
1006
  def call(arg1, arg2)
924
1007
  validate_numbers(arg1, arg2)
925
- .and_then(:validate_non_zero)
1008
+ .and_then(:validate_nonzero)
926
1009
  .and_then(:divide)
927
1010
  end
928
1011
 
@@ -935,7 +1018,7 @@ class Divide
935
1018
  Success(:numbers, [arg1, arg2])
936
1019
  end
937
1020
 
938
- def validate_non_zero(numbers)
1021
+ def validate_nonzero(numbers)
939
1022
  return Success(:numbers, numbers) unless numbers.last.zero?
940
1023
 
941
1024
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -947,10 +1030,10 @@ class Divide
947
1030
  end
948
1031
  ```
949
1032
 
950
- 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.
951
1034
 
952
1035
  > **PROTIP:**
953
- > 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.
954
1037
 
955
1038
  Now that you know the two modes, let's understand how expectations can be beneficial and powerful for defining contracts.
956
1039
 
@@ -975,7 +1058,7 @@ result.success?(:division_completed) # true
975
1058
 
976
1059
  result.success?(:ok)
977
1060
  # type :ok is not allowed. Allowed types: :numbers, :division_completed
978
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1061
+ # (BCDD::Result::Contract::Error::UnexpectedType)
979
1062
  ```
980
1063
 
981
1064
  **Failure example:**
@@ -989,7 +1072,7 @@ result.failure?(:division_by_zero) # false
989
1072
 
990
1073
  result.failure?(:err)
991
1074
  # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
992
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1075
+ # (BCDD::Result::Contract::Error::UnexpectedType)
993
1076
  ```
994
1077
 
995
1078
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -1011,7 +1094,7 @@ result
1011
1094
 
1012
1095
  result.on(:number) { |_| :this_type_does_not_exist }
1013
1096
  # type :number is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero
1014
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1097
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1015
1098
  ```
1016
1099
 
1017
1100
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -1037,11 +1120,11 @@ result
1037
1120
 
1038
1121
  result.on_success(:ok) { |_| :this_type_does_not_exist }
1039
1122
  # type :ok is not allowed. Allowed types: :numbers, :division_completed
1040
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1123
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1041
1124
 
1042
1125
  result.on_failure(:err) { |_| :this_type_does_not_exist }
1043
1126
  # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero
1044
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1127
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1045
1128
  ```
1046
1129
 
1047
1130
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -1058,17 +1141,17 @@ result = Divide.call(10, 2)
1058
1141
  result.handle do |on|
1059
1142
  on.type(:ok) { |_| :this_type_does_not_exist }
1060
1143
  end
1061
- # 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)
1062
1145
 
1063
1146
  result.handle do |on|
1064
1147
  on.success(:ok) { |_| :this_type_does_not_exist }
1065
1148
  end
1066
- # 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)
1067
1150
 
1068
1151
  result.handle do |on|
1069
1152
  on.failure(:err) { |_| :this_type_does_not_exist }
1070
1153
  end
1071
- # 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)
1072
1155
  ```
1073
1156
 
1074
1157
  *PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*
@@ -1099,11 +1182,11 @@ end
1099
1182
 
1100
1183
  Divide.call('4', 2)
1101
1184
  # type :invalid_arg is not allowed. Allowed types: :err
1102
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1185
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1103
1186
 
1104
1187
  Divide.call(4, 2)
1105
1188
  # type :division_completed is not allowed. Allowed types: :ok
1106
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1189
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1107
1190
  ```
1108
1191
 
1109
1192
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -1126,11 +1209,11 @@ end
1126
1209
 
1127
1210
  Divide.call('4', 2)
1128
1211
  # type :invalid_arg is not allowed. Allowed types: :err
1129
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1212
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1130
1213
 
1131
1214
  Divide.call(4, 2)
1132
1215
  # type :division_completed is not allowed. Allowed types: :ok
1133
- # (BCDD::Result::Expectations::Error::UnexpectedType)
1216
+ # (BCDD::Result::Contract::Error::UnexpectedType)
1134
1217
  ```
1135
1218
 
1136
1219
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -1201,26 +1284,26 @@ The value validation will only be performed through the methods `Success()` and
1201
1284
 
1202
1285
  ```ruby
1203
1286
  Divide::Result::Success(:ok)
1204
- # type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Expectations::Error::UnexpectedType)
1287
+ # type :ok is not allowed. Allowed types: :numbers, :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
1205
1288
 
1206
1289
  Divide::Result::Success(:numbers, [1])
1207
- # value [1] is not allowed for :numbers type (BCDD::Result::Expectations::Error::UnexpectedValue)
1290
+ # value [1] is not allowed for :numbers type (BCDD::Result::Contract::Error::UnexpectedValue)
1208
1291
 
1209
1292
  Divide::Result::Success(:division_completed, '2')
1210
- # value "2" is not allowed for :division_completed type (BCDD::Result::Expectations::Error::UnexpectedValue)
1293
+ # value "2" is not allowed for :division_completed type (BCDD::Result::Contract::Error::UnexpectedValue)
1211
1294
  ```
1212
1295
 
1213
1296
  ##### Failure()
1214
1297
 
1215
1298
  ```ruby
1216
1299
  Divide::Result::Failure(:err)
1217
- # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Expectations::Error::UnexpectedType)
1300
+ # type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (BCDD::Result::Contract::Error::UnexpectedType)
1218
1301
 
1219
1302
  Divide::Result::Failure(:invalid_arg, :arg1_must_be_numeric)
1220
- # value :arg1_must_be_numeric is not allowed for :invalid_arg type (BCDD::Result::Expectations::Error::UnexpectedValue)
1303
+ # value :arg1_must_be_numeric is not allowed for :invalid_arg type (BCDD::Result::Contract::Error::UnexpectedValue)
1221
1304
 
1222
1305
  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::Expectations::Error::UnexpectedValue)
1306
+ # value {:msg=>"arg2 must not be zero"} is not allowed for :division_by_zero type (BCDD::Result::Contract::Error::UnexpectedValue)
1224
1307
  ```
1225
1308
 
1226
1309
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -1231,21 +1314,14 @@ The value checking has support for handling pattern-matching errors, and the cle
1231
1314
 
1232
1315
  How does this operator work? They raise an error when the pattern does not match but returns nil when it matches.
1233
1316
 
1234
- Because of this, you will need to enable `nil` as a valid value checking. You can do it by calling the `BCDD::Result::Contract.nil_as_valid_value_checking!` method.
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.
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.
1239
1318
 
1240
1319
  ```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
1320
  module Divide
1248
1321
  extend BCDD::Result::Expectations.mixin(
1322
+ config: {
1323
+ pattern_matching: { nil_as_valid_value_checking: true }
1324
+ },
1249
1325
  success: {
1250
1326
  division_completed: ->(value) { value => (Integer | Float) }
1251
1327
  },
@@ -1263,30 +1339,30 @@ module Divide
1263
1339
  end
1264
1340
 
1265
1341
  Divide.call(10, 5)
1266
- # value "5" is not allowed for :division_completed type ("5": Float === "5" does not return true) (BCDD::Result::Contract::Error::UnexpectedValue)
1342
+ # value "2" is not allowed for :division_completed type ("2": Float === "2" does not return true) (BCDD::Result::Contract::Error::UnexpectedValue)
1267
1343
  ```
1268
1344
 
1269
1345
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1270
1346
 
1271
1347
  #### `BCDD::Result::Expectations.mixin` add-ons
1272
1348
 
1273
- 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.
1274
1350
 
1275
1351
  **Continue**
1276
1352
 
1277
- 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.
1278
1354
 
1279
1355
  ```ruby
1280
1356
  class Divide
1281
1357
  include BCDD::Result::Expectations.mixin(
1282
- with: :Continue,
1358
+ config: { addon: { continue: true } },
1283
1359
  success: :division_completed,
1284
1360
  failure: %i[invalid_arg division_by_zero]
1285
1361
  )
1286
1362
 
1287
1363
  def call(arg1, arg2)
1288
1364
  validate_numbers(arg1, arg2)
1289
- .and_then(:validate_non_zero)
1365
+ .and_then(:validate_nonzero)
1290
1366
  .and_then(:divide)
1291
1367
  end
1292
1368
 
@@ -1299,7 +1375,7 @@ class Divide
1299
1375
  Continue([arg1, arg2])
1300
1376
  end
1301
1377
 
1302
- def validate_non_zero(numbers)
1378
+ def validate_nonzero(numbers)
1303
1379
  return Continue(numbers) unless numbers.last.zero?
1304
1380
 
1305
1381
  Failure(:division_by_zero, 'arg2 must not be zero')
@@ -1310,7 +1386,7 @@ class Divide
1310
1386
  end
1311
1387
  end
1312
1388
 
1313
- result = Divide.new.call(4,2)
1389
+ result = Divide.new.call(4, 2)
1314
1390
  # => #<BCDD::Result::Success type=:division_completed value=2>
1315
1391
 
1316
1392
  # The example below shows an error because the :ok type is not allowed.
@@ -1318,7 +1394,7 @@ result = Divide.new.call(4,2)
1318
1394
  # This is because the :continued type is ignored by the expectations.
1319
1395
  #
1320
1396
  result.success?(:ok)
1321
- # 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)
1322
1398
  ```
1323
1399
 
1324
1400
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
@@ -1368,14 +1444,14 @@ Let's see this feature and the data accumulation in action:
1368
1444
  ##### Class example (Instance Methods)
1369
1445
 
1370
1446
  ```ruby
1371
- class Divide
1372
- require 'logger'
1447
+ require 'logger'
1373
1448
 
1449
+ class Divide
1374
1450
  include BCDD::Result::Context.mixin
1375
1451
 
1376
1452
  def call(arg1, arg2, logger: ::Logger.new(STDOUT))
1377
1453
  validate_numbers(arg1, arg2)
1378
- .and_then(:validate_non_zero)
1454
+ .and_then(:validate_nonzero)
1379
1455
  .and_then(:divide, logger: logger)
1380
1456
  end
1381
1457
 
@@ -1388,7 +1464,7 @@ class Divide
1388
1464
  Success(:ok, number1: arg1, number2: arg2)
1389
1465
  end
1390
1466
 
1391
- def validate_non_zero(number2:, **)
1467
+ def validate_nonzero(number2:, **)
1392
1468
  return Success(:ok) if number2.nonzero?
1393
1469
 
1394
1470
  Failure(:err, message: 'arg2 must not be zero')
@@ -1434,8 +1510,9 @@ class Divide
1434
1510
 
1435
1511
  def call(arg1, arg2)
1436
1512
  validate_numbers(arg1, arg2)
1437
- .and_then(:validate_non_zero)
1513
+ .and_then(:validate_nonzero)
1438
1514
  .and_then(:divide)
1515
+ .and_expose(:division_completed, [:number])
1439
1516
  end
1440
1517
 
1441
1518
  private
@@ -1447,7 +1524,7 @@ class Divide
1447
1524
  Success(:ok, number1: arg1, number2: arg2)
1448
1525
  end
1449
1526
 
1450
- def validate_non_zero(number2:, **)
1527
+ def validate_nonzero(number2:, **)
1451
1528
  return Success(:ok) if number2.nonzero?
1452
1529
 
1453
1530
  Failure(:err, message: 'arg2 must not be zero')
@@ -1483,7 +1560,7 @@ module Divide
1483
1560
 
1484
1561
  def call(arg1, arg2)
1485
1562
  validate_numbers(arg1, arg2)
1486
- .and_then(:validate_non_zero)
1563
+ .and_then(:validate_nonzero)
1487
1564
  .and_then(:divide)
1488
1565
  .and_expose(:division_completed, [:number])
1489
1566
  end
@@ -1497,7 +1574,7 @@ module Divide
1497
1574
  Success(:ok, number1: arg1, number2: arg2)
1498
1575
  end
1499
1576
 
1500
- def validate_non_zero(number2:, **)
1577
+ def validate_nonzero(number2:, **)
1501
1578
  return Success(:ok) if number2.nonzero?
1502
1579
 
1503
1580
  Failure(:err, message: 'arg2 must not be zero')
@@ -1530,14 +1607,11 @@ The `BCDD::Result::Context::Expectations` is a `BCDD::Result::Expectations` with
1530
1607
  This is an example using the mixin mode, but the standalone mode is also supported.
1531
1608
 
1532
1609
  ```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
1610
  class Divide
1540
1611
  include BCDD::Result::Context::Expectations.mixin(
1612
+ config: {
1613
+ pattern_matching: { nil_as_valid_value_checking: true }
1614
+ },
1541
1615
  success: {
1542
1616
  division_completed: ->(value) { value => { number: Numeric } }
1543
1617
  },
@@ -1553,7 +1627,7 @@ class Divide
1553
1627
 
1554
1628
  arg2.zero? and return Failure(:division_by_zero, message: 'arg2 must not be zero')
1555
1629
 
1556
- Success(:division_completed, number: arg1 / arg2)
1630
+ Success(:division_completed, number: (arg1 / arg2))
1557
1631
  end
1558
1632
  end
1559
1633
 
@@ -1577,26 +1651,23 @@ Divide::Result::Success(:division_completed, number: '2')
1577
1651
 
1578
1652
  #### Mixin add-ons
1579
1653
 
1580
- The `BCDD::Result::Context.mixin` and `BCDD::Result::Context::Expectations.mixin` also accepts the `with:` argument. And it works the same way as the `BCDD::Result` mixins.
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.
1581
1655
 
1582
1656
  **Continue**
1583
1657
 
1584
- The `BCDD::Result::Context.mixin(with: :Continue)` or `BCDD::Result::Context::Expectations.mixin(with: :Continue)` 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.
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.
1585
1659
 
1586
1660
  Let's use a mix of `BCDD::Result::Context` features to see in action with this add-on:
1587
1661
 
1588
1662
  ```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
1663
  module Divide
1596
1664
  require 'logger'
1597
1665
 
1598
1666
  extend self, BCDD::Result::Context::Expectations.mixin(
1599
- with: :Continue,
1667
+ config: {
1668
+ addon: { continue: true },
1669
+ pattern_matching: { nil_as_valid_value_checking: true }
1670
+ },
1600
1671
  success: {
1601
1672
  division_completed: ->(value) { value => { number: Numeric } }
1602
1673
  },
@@ -1608,7 +1679,7 @@ module Divide
1608
1679
 
1609
1680
  def call(arg1, arg2, logger: ::Logger.new(STDOUT))
1610
1681
  validate_numbers(arg1, arg2)
1611
- .and_then(:validate_non_zero)
1682
+ .and_then(:validate_nonzero)
1612
1683
  .and_then(:divide, logger: logger)
1613
1684
  .and_expose(:division_completed, [:number])
1614
1685
  end
@@ -1622,7 +1693,7 @@ module Divide
1622
1693
  Continue(number1: arg1, number2: arg2)
1623
1694
  end
1624
1695
 
1625
- def validate_non_zero(number2:, **)
1696
+ def validate_nonzero(number2:, **)
1626
1697
  return Continue() if number2.nonzero?
1627
1698
 
1628
1699
  Failure(:division_by_zero, message: 'arg2 must not be zero')
@@ -1651,8 +1722,119 @@ Divide.call(14, 0)
1651
1722
  #<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
1652
1723
  ```
1653
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
+
1654
1757
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1655
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
+
1656
1838
  ## About
1657
1839
 
1658
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.