bcdd-result 0.9.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/CHANGELOG.md +43 -16
  4. data/README.md +259 -40
  5. data/Rakefile +8 -2
  6. data/Steepfile +2 -2
  7. data/lib/bcdd/result/config/switchers/addons.rb +25 -0
  8. data/lib/bcdd/result/config/{constant_alias.rb → switchers/constant_aliases.rb} +4 -4
  9. data/lib/bcdd/result/config/switchers/features.rb +28 -0
  10. data/lib/bcdd/result/config/switchers/pattern_matching.rb +20 -0
  11. data/lib/bcdd/result/config.rb +8 -28
  12. data/lib/bcdd/result/context/expectations/mixin.rb +10 -2
  13. data/lib/bcdd/result/context/mixin.rb +13 -5
  14. data/lib/bcdd/result/context/success.rb +2 -2
  15. data/lib/bcdd/result/context.rb +10 -15
  16. data/lib/bcdd/result/expectations/mixin.rb +11 -5
  17. data/lib/bcdd/result/expectations.rb +6 -6
  18. data/lib/bcdd/result/mixin.rb +11 -5
  19. data/lib/bcdd/result/transitions/tracking/disabled.rb +17 -0
  20. data/lib/bcdd/result/transitions/tracking/enabled.rb +80 -0
  21. data/lib/bcdd/result/transitions/tracking.rb +20 -0
  22. data/lib/bcdd/result/transitions/tree.rb +95 -0
  23. data/lib/bcdd/result/transitions.rb +30 -0
  24. data/lib/bcdd/result/version.rb +1 -1
  25. data/lib/bcdd/result.rb +33 -22
  26. data/sig/bcdd/result/config.rbs +101 -0
  27. data/sig/bcdd/result/context.rbs +114 -0
  28. data/sig/bcdd/result/contract.rbs +119 -0
  29. data/sig/bcdd/result/data.rbs +16 -0
  30. data/sig/bcdd/result/error.rbs +31 -0
  31. data/sig/bcdd/result/expectations.rbs +71 -0
  32. data/sig/bcdd/result/handler.rbs +47 -0
  33. data/sig/bcdd/result/mixin.rbs +45 -0
  34. data/sig/bcdd/result/transitions.rbs +89 -0
  35. data/sig/bcdd/result/version.rbs +5 -0
  36. data/sig/bcdd/result.rbs +7 -519
  37. metadata +22 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78ccef0bd217127baf1bf96cabf4074c2ab2e8bb7befffd7c7f2f3df4631bf27
4
- data.tar.gz: '0429c37305f2265dd13517c7a64e977929f6a4bde4b70421de01c8a418b67656'
3
+ metadata.gz: c0043be8de0b26bac0d803f067537fbc65e4a54f3a38a2644c01bd987d87c7b5
4
+ data.tar.gz: f152ac78abb8847721f2b2ec3c3fd1db66016c421f5348f038a79501a86f761a
5
5
  SHA512:
6
- metadata.gz: f8756ac806f830d458b3c830c15ac667cbb29f1737fa97cb59870541e2f3e8000839ab775c16559370499a7055db7f0938bb8a71071db9b5392507af5f0ec7de
7
- data.tar.gz: 1003e299eec64ddb62a51b1f8d358b9dca2b58add3f8b4b9a4c46a5a70cb825907b2de7339c4f5e94817fb92e48c3a702eae67cd6eeca80942c60bdc80dff071
6
+ metadata.gz: 17abccc11ad3c7ccd2cedc6f9634659decee457e08ef1c8dfb13d59d2afc7390fdeb6fc1d48749362d691815c41b789197d2bf79b5f57f9e7e07ad770ddbb304
7
+ data.tar.gz: 2e0b67fbc99ef2a3a8187a2d42212e8cbadca22a2c2625c3be8e7bbfb5d2b92689b9a27b7592a295863e89437e912d34cb8f6e5a2e77e9c0afdbcbf7c4591bd6
data/.rubocop.yml CHANGED
@@ -9,6 +9,10 @@ AllCops:
9
9
  NewCops: enable
10
10
  TargetRubyVersion: 2.7
11
11
 
12
+ Lint/RescueException:
13
+ Exclude:
14
+ - lib/bcdd/result/transitions.rb
15
+
12
16
  Layout/LineLength:
13
17
  Max: 120
14
18
 
@@ -70,6 +74,10 @@ Minitest/AssertEmptyLiteral:
70
74
  Minitest/AssertOperator:
71
75
  Enabled: false
72
76
 
77
+ Minitest/AssertWithExpectedArgument:
78
+ Exclude:
79
+ - test/test_helper.rb
80
+
73
81
  Naming/FileName:
74
82
  Exclude:
75
83
  - lib/bcdd-result.rb
data/CHANGELOG.md CHANGED
@@ -1,41 +1,68 @@
1
1
  - [\[Unreleased\]](#unreleased)
2
- - [\[0.9.1\] - 2023-12-12](#091---2023-12-12)
2
+ - [\[0.11.0\] - 2024-01-02](#0110---2024-01-02)
3
+ - [Added](#added)
3
4
  - [Changed](#changed)
5
+ - [\[0.10.0\] - 2023-12-31](#0100---2023-12-31)
6
+ - [Added](#added-1)
7
+ - [\[0.9.1\] - 2023-12-12](#091---2023-12-12)
8
+ - [Changed](#changed-1)
4
9
  - [Fixed](#fixed)
5
10
  - [\[0.9.0\] - 2023-12-12](#090---2023-12-12)
6
- - [Added](#added)
7
- - [Changed](#changed-1)
8
- - [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
9
- - [Added](#added-1)
11
+ - [Added](#added-2)
10
12
  - [Changed](#changed-2)
13
+ - [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
14
+ - [Added](#added-3)
15
+ - [Changed](#changed-3)
11
16
  - [Removed](#removed)
12
17
  - [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
13
- - [Added](#added-2)
14
- - [Changed](#changed-3)
15
- - [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
16
- - [Added](#added-3)
17
- - [Changed](#changed-4)
18
- - [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
19
18
  - [Added](#added-4)
20
- - [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
19
+ - [Changed](#changed-4)
20
+ - [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
21
21
  - [Added](#added-5)
22
22
  - [Changed](#changed-5)
23
+ - [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
24
+ - [Added](#added-6)
25
+ - [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
26
+ - [Added](#added-7)
27
+ - [Changed](#changed-6)
23
28
  - [Removed](#removed-1)
24
29
  - [\[0.3.0\] - 2023-09-26](#030---2023-09-26)
25
- - [Added](#added-6)
30
+ - [Added](#added-8)
26
31
  - [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
27
- - [Added](#added-7)
32
+ - [Added](#added-9)
28
33
  - [Removed](#removed-2)
29
34
  - [\[0.1.0\] - 2023-09-25](#010---2023-09-25)
30
- - [Added](#added-8)
35
+ - [Added](#added-10)
31
36
 
32
37
  ## [Unreleased]
33
38
 
39
+ ## [0.11.0] - 2024-01-02
40
+
41
+ ### Added
42
+
43
+ - Add the `Given()` addon to produce a `Success(:given, value)` result. As the `Continue()` addon, it is ignored by the expectations. Use it to add a value to the result chain and invoke the next step (through `and_then`).
44
+
45
+ ### Changed
46
+
47
+ - **(BREAKING)** Rename halted concept to terminal. Failures are terminal by default, but you can make a success terminal by enabling the `:continue` addon.
48
+
49
+ - **(BREAKING)** Rename `BCDD::Result::Context::Success#and_expose` halted keyword argument to `terminal`.
50
+
51
+ - **(BREAKING)** Rename `BCDD::Result#halted?` to `BCDD::Result#terminal?`.
52
+
53
+ ## [0.10.0] - 2023-12-31
54
+
55
+ ### Added
56
+
57
+ - Add `BCDD::Result.transitions(&block)` to track all transitions in the same or between different operations. When there is a nesting of transition blocks, this mechanism will be able to correlate parent and child blocks and present the duration of all operations in milliseconds.
58
+
59
+ - Add `BCDD::Result.config.feature.disable!(:transitions)` and `BCDD::Result.config.feature.enable!(:transitions)` to turn on/off the `BCDD::Result.transitions` feature.
60
+
34
61
  ## [0.9.1] - 2023-12-12
35
62
 
36
63
  ### Changed
37
64
 
38
- - **(BREAKING)** Make `BCDD::Result::Context::Success#and_expose()` to produce a halted success by default. You can turn this off by passing `halted: false`.
65
+ - **(BREAKING)** Make `BCDD::Result::Context::Success#and_expose()` to produce a terminal success by default. You can turn this off by passing `halted: false`.
39
66
 
40
67
  ### Fixed
41
68
 
data/README.md CHANGED
@@ -70,8 +70,10 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
70
70
  - [Module example (Singleton Methods)](#module-example-singleton-methods-1)
71
71
  - [`BCDD::Result::Context::Expectations`](#bcddresultcontextexpectations)
72
72
  - [Mixin add-ons](#mixin-add-ons)
73
+ - [`BCDD::Result.transitions`](#bcddresulttransitions)
74
+ - [Configuration](#configuration)
73
75
  - [`BCDD::Result.configuration`](#bcddresultconfiguration)
74
- - [`config.addon.enable!(:continue)`](#configaddonenablecontinue)
76
+ - [`config.addon.enable!(:given, :continue)`](#configaddonenablegiven-continue)
75
77
  - [`config.constant_alias.enable!('Result', 'BCDD::Context')`](#configconstant_aliasenableresult-bcddcontext)
76
78
  - [`config.pattern_matching.disable!(:nil_as_valid_value_checking)`](#configpattern_matchingdisablenil_as_valid_value_checking)
77
79
  - [`config.feature.disable!(:expectations)`](#configfeaturedisableexpectations)
@@ -916,35 +918,48 @@ Divide.call(4, 2, logger: Logger.new(IO::NULL))
916
918
 
917
919
  The `BCDD::Result.mixin` also accepts the `config:` argument. It is a hash that will be used to define custom behaviors for the mixin.
918
920
 
921
+ **given**
922
+
923
+ This addon is enabled by default. It will create the `Given(value)` method. Use it to add a value to the result chain and invoke the next step (through `and_then`).
924
+
925
+ You can turn it off by passing `given: false` to the `config:` argument or using the `BCDD::Result.configuration`.
926
+
919
927
  **continue**
920
928
 
921
- This addon will create the `Continue(value)` method and change the `Success()` behavior to halt the step chain.
929
+ This addon will create the `Continue(value)` method and change the `Success()` behavior to terminate the step chain.
922
930
 
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.
931
+ 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 terminated.
932
+
933
+ In this example below, the `validate_nonzero` will return a `Success(:division_completed, 0)` and terminate the chain if the first number is zero.
924
934
 
925
935
  ```ruby
926
936
  module Divide
927
937
  extend self, BCDD::Result.mixin(config: { addon: { continue: true } })
928
938
 
929
939
  def call(arg1, arg2)
930
- validate_numbers(arg1, arg2)
940
+ Given([arg1, arg2])
941
+ .and_then(:validate_numbers)
931
942
  .and_then(:validate_nonzero)
932
943
  .and_then(:divide)
933
944
  end
934
945
 
935
946
  private
936
947
 
937
- def validate_numbers(arg1, arg2)
938
- arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
939
- arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
948
+ def validate_numbers(numbers)
949
+ number1, number2 = numbers
940
950
 
941
- Continue([arg1, arg2])
951
+ number1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
952
+ number2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
953
+
954
+ Continue(numbers)
942
955
  end
943
956
 
944
957
  def validate_nonzero(numbers)
945
- return Continue(numbers) unless numbers.last.zero?
958
+ return Failure(:division_by_zero, 'arg2 must not be zero') if numbers.last.zero?
946
959
 
947
- Failure(:division_by_zero, 'arg2 must not be zero')
960
+ return Success(:division_completed, 0) if numbers.first.zero?
961
+
962
+ Continue(numbers)
948
963
  end
949
964
 
950
965
  def divide((number1, number2))
@@ -983,7 +998,7 @@ module Divide
983
998
  end
984
999
  ```
985
1000
 
986
- 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.
1001
+ In the code above, we define a constant `Divide::Result`. And because of this (it is a constant), we can use it inside and outside the module.
987
1002
 
988
1003
  Look what happens if you try to create a result without one of the expected types.
989
1004
 
@@ -1223,7 +1238,7 @@ Divide.call(4, 2)
1223
1238
 
1224
1239
  #### Value checking - Result Creation
1225
1240
 
1226
- The `Result::Expectations` supports types of validations. The first is the type checking only, and the second is the type and value checking.
1241
+ The `Result::Expectations` supports two types of validations. The first is the type checking only, and the second is the type and value checking.
1227
1242
 
1228
1243
  To define expectations for your result's values, you must declare a Hash with the type as the key and the value as the value. A value validator is any object that responds to `#===` (case equality operator).
1229
1244
 
@@ -1569,13 +1584,13 @@ Divide.new.call(10, 5)
1569
1584
  #<BCDD::Result::Context::Success type=:ok value={:number=>2, :number1=>10, :number2=>5}>
1570
1585
  ```
1571
1586
 
1572
- > PS: The `#and_expose` produces a halted success by default. This means the next step will not be executed even if you call `#and_then` after `#and_expose`. To change this behavior, you can pass `halted: false` to `#and_expose`.
1587
+ > PS: The `#and_expose` produces a terminal success by default. This means the next step will not be executed even if you call `#and_then` after `#and_expose`. To change this behavior, you can pass `terminal: false` to `#and_expose`.
1573
1588
 
1574
1589
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1575
1590
 
1576
1591
  ##### Module example (Singleton Methods)
1577
1592
 
1578
- Yes, the `BCDD::Result::Context.mixin` can produce singleton methods. Look for an example using a module (but it could be a class, too).
1593
+ `BCDD::Result::Context.mixin` can also produce singleton methods. Below is an example using a module (but it could be a class, too).
1579
1594
 
1580
1595
  ```ruby
1581
1596
  module Divide
@@ -1660,7 +1675,7 @@ Divide.new.call(10, 5)
1660
1675
 
1661
1676
  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.
1662
1677
 
1663
- Let's see this using previous example:
1678
+ Let's see this using the previous example:
1664
1679
 
1665
1680
  ```ruby
1666
1681
  Divide::Result::Success(:division_completed, number: 2)
@@ -1676,16 +1691,24 @@ Divide::Result::Success(:division_completed, number: '2')
1676
1691
 
1677
1692
  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.
1678
1693
 
1679
- **Continue**
1694
+ **given**
1695
+
1696
+ This addon is enabled by default. It will create the `Given(*value)` method. Use it to add a value to the result chain and invoke the next step (through `and_then`).
1697
+
1698
+ You can turn it off by passing `given: false` to the `config:` argument or using the `BCDD::Result.configuration`.
1699
+
1700
+ The `Given()` addon for a BCDD::Result::Context can be called with one or more arguments. The arguments will be converted to a hash (`to_h`) and merged to define the first value of the result chain.
1701
+
1702
+ **continue**
1680
1703
 
1681
- 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.
1704
+ 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 terminate the step chain.
1682
1705
 
1683
- 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.
1706
+ 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 terminated.
1684
1707
 
1685
1708
  Let's use a mix of `BCDD::Result::Context` features to see in action with this add-on:
1686
1709
 
1687
1710
  ```ruby
1688
- module Divide
1711
+ module Division
1689
1712
  require 'logger'
1690
1713
 
1691
1714
  extend self, BCDD::Result::Context::Expectations.mixin(
@@ -1703,25 +1726,28 @@ module Divide
1703
1726
  )
1704
1727
 
1705
1728
  def call(arg1, arg2, logger: ::Logger.new(STDOUT))
1706
- validate_numbers(arg1, arg2)
1707
- .and_then(:validate_nonzero)
1729
+ Given(number1: arg1, number2: arg2)
1730
+ .and_then(:require_numbers)
1731
+ .and_then(:check_for_zeros)
1708
1732
  .and_then(:divide, logger: logger)
1709
1733
  .and_expose(:division_completed, [:number])
1710
1734
  end
1711
1735
 
1712
1736
  private
1713
1737
 
1714
- def validate_numbers(arg1, arg2)
1715
- arg1.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg1 must be numeric')
1716
- arg2.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg2 must be numeric')
1738
+ def require_numbers(number1:, number2:)
1739
+ number1.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg1 must be numeric')
1740
+ number2.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg2 must be numeric')
1717
1741
 
1718
- Continue(number1: arg1, number2: arg2)
1742
+ Continue()
1719
1743
  end
1720
1744
 
1721
- def validate_nonzero(number2:, **)
1722
- return Continue() if number2.nonzero?
1745
+ def check_for_zeros(number1:, number2:)
1746
+ return Failure(:division_by_zero, message: 'arg2 must not be zero') if number2.zero?
1723
1747
 
1724
- Failure(:division_by_zero, message: 'arg2 must not be zero')
1748
+ return Success(:division_completed, number: 0) if number1.zero?
1749
+
1750
+ Continue()
1725
1751
  end
1726
1752
 
1727
1753
  def divide(number1:, number2:, logger:)
@@ -1733,27 +1759,201 @@ module Divide
1733
1759
  end
1734
1760
  end
1735
1761
 
1736
- Divide.call(14, 2)
1762
+ Division.call(14, 2)
1737
1763
  # I, [2023-10-27T02:01:05.812388 #77823] INFO -- : The division result is 7
1738
1764
  #<BCDD::Result::Context::Success type=:division_completed value={:number=>7}>
1739
1765
 
1740
- Divide.call('14', 2)
1766
+ Division.call(0, 2)
1767
+ ##<BCDD::Result::Context::Success type=:division_completed value={:number=>0}>
1768
+
1769
+ Division.call('14', 2)
1741
1770
  #<BCDD::Result::Context::Failure type=:invalid_arg value={:message=>"arg1 must be numeric"}>
1742
1771
 
1743
- Divide.call(14, '2')
1772
+ Division.call(14, '2')
1744
1773
  #<BCDD::Result::Context::Failure type=:invalid_arg value={:message=>"arg2 must be numeric"}>
1745
1774
 
1746
- Divide.call(14, 0)
1775
+ Division.call(14, 0)
1747
1776
  #<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
1748
1777
  ```
1749
1778
 
1779
+ ### `BCDD::Result.transitions`
1780
+
1781
+ Use `BCDD::Result.transitions(&block)` to track all transitions in the same or between different operations (it works with `BCDD::Result` and `BCDD::Result::Context`). When there is a nesting of transition blocks, this mechanism will be able to correlate parent and child blocks and present the duration of all operations in milliseconds.
1782
+
1783
+ When you wrap the creation of the result with `BCDD::Result.transitions`, the final result will expose all the transition records through the `BCDD::Result#transitions` method.
1784
+
1785
+ ```ruby
1786
+ class Division
1787
+ include BCDD::Result.mixin(config: { addon: { continue: true } })
1788
+
1789
+ def call(arg1, arg2)
1790
+ BCDD::Result.transitions(name: 'Division', desc: 'divide two numbers') do
1791
+ Given([arg1, arg2])
1792
+ .and_then(:require_numbers)
1793
+ .and_then(:check_for_zeros)
1794
+ .and_then(:divide)
1795
+ end
1796
+ end
1797
+
1798
+ private
1799
+
1800
+ ValidNumber = ->(arg) { arg.is_a?(Numeric) && arg != Float::NAN && arg != Float::INFINITY }
1801
+
1802
+ def require_numbers((arg1, arg2))
1803
+ ValidNumber[arg1] or return Failure(:invalid_arg, 'arg1 must be a valid number')
1804
+ ValidNumber[arg2] or return Failure(:invalid_arg, 'arg2 must be a valid number')
1805
+
1806
+ Continue([arg1, arg2])
1807
+ end
1808
+
1809
+ def check_for_zeros(numbers)
1810
+ num1, num2 = numbers
1811
+
1812
+ return Failure(:division_by_zero, 'num2 cannot be zero') if num2.zero?
1813
+
1814
+ num1.zero? ? Success(:division_completed, 0) : Continue(numbers)
1815
+ end
1816
+
1817
+ def divide((num1, num2))
1818
+ Success(:division_completed, num1 / num2)
1819
+ end
1820
+ end
1821
+
1822
+ module SumDivisionsByTwo
1823
+ extend self, BCDD::Result.mixin
1824
+
1825
+ def call(*numbers)
1826
+ BCDD::Result.transitions(name: 'SumDivisionsByTwo') do
1827
+ divisions = numbers.map { |number| Division.new.call(number, 2) }
1828
+
1829
+ if divisions.any?(&:failure?)
1830
+ Failure(:errors, divisions.select(&:failure?).map(&:value))
1831
+ else
1832
+ Success(:sum, divisions.sum(&:value))
1833
+ end
1834
+ end
1835
+ end
1836
+ end
1837
+ ```
1838
+
1839
+ Let's see the result of the `SumDivisionsByTwo` call:
1840
+
1841
+ ```ruby
1842
+ result = SumDivisionsByTwo.call(20, 10)
1843
+ # => #<BCDD::Result::Success type=:sum value=15>
1844
+
1845
+ result.transitions
1846
+ {
1847
+ :version =>1,
1848
+ :metadata => {
1849
+ :duration => 0, # milliseconds
1850
+ :tree_map => [0, [[1, []], [2, []]]], # represents the tree of transitions using the id of each transition block
1851
+ },
1852
+ :records => [
1853
+ {
1854
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1855
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1856
+ :current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
1857
+ :result=>{:kind=>:success, :type=>:given, :value=>[20, 2]},
1858
+ :and_then=>{},
1859
+ :time=>2024-01-02 03:35:11.248418 UTC
1860
+ },
1861
+ {
1862
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1863
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1864
+ :current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
1865
+ :result=>{:kind=>:success, :type=>:continued, :value=>[20, 2]},
1866
+ :and_then=>{:type=>:method, :arg=>nil, :subject=><Division:0x0000000106099028>, :method_name=>:require_numbers},
1867
+ :time=>2024-01-02 03:35:11.248558 UTC
1868
+ },
1869
+ {
1870
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1871
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1872
+ :current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
1873
+ :result=>{:kind=>:success, :type=>:continued, :value=>[20, 2]},
1874
+ :and_then=>{:type=>:method, :arg=>nil, :subject=><Division:0x0000000106099028>, :method_name=>:check_for_zeros},
1875
+ :time=>2024-01-02 03:35:11.248587 UTC
1876
+ },
1877
+ {
1878
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1879
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1880
+ :current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
1881
+ :result=>{:kind=>:success, :type=>:division_completed, :value=>10},
1882
+ :and_then=>{:type=>:method, :arg=>nil, :subject=><Division:0x0000000106099028>, :method_name=>:divide},
1883
+ :time=>2024-01-02 03:35:11.248607 UTC
1884
+ },
1885
+ {
1886
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1887
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1888
+ :current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
1889
+ :result=>{:kind=>:success, :type=>:given, :value=>[10, 2]},
1890
+ :and_then=>{},
1891
+ :time=>2024-01-02 03:35:11.24865 UTC
1892
+ },
1893
+ {
1894
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1895
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1896
+ :current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
1897
+ :result=>{:kind=>:success, :type=>:continued, :value=>[10, 2]},
1898
+ :and_then=>{:type=>:method, :arg=>nil, :subject=><Division:0x0000000106097ed0>, :method_name=>:require_numbers},
1899
+ :time=>2024-01-02 03:35:11.248661 UTC
1900
+ },
1901
+ {
1902
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1903
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1904
+ :current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
1905
+ :result=>{:kind=>:success, :type=>:continued, :value=>[10, 2]},
1906
+ :and_then=>{:type=>:method, :arg=>nil, :subject=><Division:0x0000000106097ed0>, :method_name=>:check_for_zeros},
1907
+ :time=>2024-01-02 03:35:11.248672 UTC
1908
+ },
1909
+ {
1910
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1911
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1912
+ :current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
1913
+ :result=>{:kind=>:success, :type=>:division_completed, :value=>5},
1914
+ :and_then=>{:type=>:method, :arg=>nil, :subject=><Division:0x0000000106097ed0>, :method_name=>:divide},
1915
+ :time=>2024-01-02 03:35:11.248682 UTC
1916
+ },
1917
+ {
1918
+ :root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1919
+ :parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1920
+ :current=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
1921
+ :result=>{:kind=>:success, :type=>:sum, :value=>15},
1922
+ :and_then=>{},
1923
+ :time=>2024-01-02 03:35:11.248721 UTC
1924
+ }
1925
+ ]
1926
+ }
1927
+ ```
1928
+
1929
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1930
+
1931
+ #### Configuration
1932
+
1933
+ You can use `BCDD::Result.config.feature.disable!(:transitions)` and `BCDD::Result.config.feature.enable!(:transitions)` to turn on/off the `BCDD::Result.transitions` feature.
1934
+
1935
+ ```ruby
1936
+ BCDD::Result.configuration do |config|
1937
+ config.feature.disable!(:transitions)
1938
+ end
1939
+
1940
+ result = SumDivisionsByTwo.call(20, 10)
1941
+ # => #<BCDD::Result::Success type=:sum value=15>
1942
+
1943
+ result.transitions
1944
+
1945
+ {:version=>1, :records=>[], :metadata=>{:duration=>0, :tree_map=>[]}}
1946
+ ```
1947
+
1948
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1949
+
1750
1950
  ### `BCDD::Result.configuration`
1751
1951
 
1752
1952
  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.
1753
1953
 
1754
1954
  ```ruby
1755
1955
  BCDD::Result.configuration do |config|
1756
- config.addon.enable!(:continue)
1956
+ config.addon.enable!(:given, :continue)
1757
1957
 
1758
1958
  config.constant_alias.enable!('Result', 'BCDD::Context')
1759
1959
 
@@ -1767,9 +1967,11 @@ Use `disable!` to disable a feature and `enable!` to enable it.
1767
1967
 
1768
1968
  Let's see what each configuration in the example above does:
1769
1969
 
1770
- #### `config.addon.enable!(:continue)`
1970
+ #### `config.addon.enable!(:given, :continue)`
1971
+
1972
+ This configuration enables the `Continue()` method for `BCDD::Result.mixin`, `BCDD::Result::Context.mixin`, `BCDD::Result::Expectation.mixin`, and `BCDD::Result::Context::Expectation.mixin`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).
1771
1973
 
1772
- 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).
1974
+ It is also enabling the `Given()` which is already enabled by default. Link to documentation: [(1)](#add-ons) [(2)](#mixin-add-ons).
1773
1975
 
1774
1976
  #### `config.constant_alias.enable!('Result', 'BCDD::Context')`
1775
1977
 
@@ -1793,22 +1995,32 @@ PS: I'm using `::Rails.env.production?` to check the environment, but you can us
1793
1995
 
1794
1996
  ### `BCDD::Result.config`
1795
1997
 
1796
- The `BCDD::Result.config` allows you to access the current configuration. It is useful when you want to check the current configuration.
1998
+ The `BCDD::Result.config` allows you to access the current configuration.
1797
1999
 
1798
2000
  **BCDD::Result.config.addon**
1799
2001
 
1800
2002
  ```ruby
1801
2003
  BCDD::Result.config.addon.enabled?(:continue)
2004
+ BCDD::Result.config.addon.enabled?(:given)
1802
2005
 
1803
2006
  BCDD::Result.config.addon.options
1804
2007
  # {
1805
2008
  # :continue=>{
1806
2009
  # :enabled=>false,
1807
2010
  # :affects=>[
1808
- # "BCDD::Result",
1809
- # "BCDD::Result::Context",
1810
- # "BCDD::Result::Expectations",
1811
- # "BCDD::Result::Context::Expectations"
2011
+ # "BCDD::Result.mixin",
2012
+ # "BCDD::Result::Context.mixin",
2013
+ # "BCDD::Result::Expectations.mixin",
2014
+ # "BCDD::Result::Context::Expectations.mixin"
2015
+ # ]
2016
+ # },
2017
+ # :given=>{
2018
+ # :enabled=>true,
2019
+ # :affects=>[
2020
+ # "BCDD::Result.mixin",
2021
+ # "BCDD::Result::Context.mixin",
2022
+ # "BCDD::Result::Expectations.mixin",
2023
+ # "BCDD::Result::Context::Expectations.mixin"
1812
2024
  # ]
1813
2025
  # }
1814
2026
  # }
@@ -1859,6 +2071,13 @@ BCDD::Result.config.feature.options
1859
2071
  # "BCDD::Result::Expectations,
1860
2072
  # "BCDD::Result::Context::Expectations"
1861
2073
  # ]
2074
+ # },
2075
+ # :transitions=>{
2076
+ # :enabled=>true,
2077
+ # :affects=>[
2078
+ # "BCDD::Result",
2079
+ # "BCDD::Result::Context"
2080
+ # ]
1862
2081
  # }
1863
2082
  # }
1864
2083
  ```
data/Rakefile CHANGED
@@ -3,16 +3,22 @@
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rake/testtask'
5
5
 
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs += %w[lib test]
8
+
9
+ t.test_files = FileList.new('test/**/*_test.rb')
10
+ end
11
+
6
12
  Rake::TestTask.new(:test_configuration) do |t|
7
13
  t.libs += %w[lib test]
8
14
 
9
15
  t.test_files = FileList.new('test/**/configuration_test.rb')
10
16
  end
11
17
 
12
- Rake::TestTask.new(:test) do |t|
18
+ Rake::TestTask.new(:test_transitions_duration) do |t|
13
19
  t.libs += %w[lib test]
14
20
 
15
- t.test_files = FileList.new('test/**/*_test.rb')
21
+ t.test_files = FileList.new('test/**/duration_test.rb')
16
22
  end
17
23
 
18
24
  require 'rubocop/rake_task'
data/Steepfile CHANGED
@@ -10,8 +10,8 @@ target :lib do
10
10
  # check 'app/models/**/*.rb' # Glob
11
11
  # ignore 'lib/templates/*.rb'
12
12
 
13
- library 'singleton' # Standard libraries
14
- # library 'strong_json' # Gems
13
+ library 'singleton', 'securerandom' # Standard libraries
14
+ # library 'strong_json' # Gems
15
15
 
16
16
  # configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
17
17
  # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BCDD::Result
4
+ class Config
5
+ module Addons
6
+ AFFECTS = %w[
7
+ BCDD::Result.mixin
8
+ BCDD::Result::Context.mixin
9
+ BCDD::Result::Expectations.mixin
10
+ BCDD::Result::Context::Expectations.mixin
11
+ ].freeze
12
+
13
+ OPTIONS = {
14
+ continue: { default: false, affects: AFFECTS },
15
+ given: { default: true, affects: AFFECTS }
16
+ }.transform_values!(&:freeze).freeze
17
+
18
+ def self.switcher
19
+ Switcher.new(options: OPTIONS)
20
+ end
21
+ end
22
+
23
+ private_constant :Addons
24
+ end
25
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  class BCDD::Result
4
4
  class Config
5
- module ConstantAlias
5
+ module ConstantAliases
6
6
  MAPPING = {
7
7
  'Result' => { target: ::Object, name: :Result, value: ::BCDD::Result },
8
8
  'Context' => { target: ::Object, name: :Context, value: ::BCDD::Result::Context },
@@ -15,14 +15,14 @@ class BCDD::Result
15
15
  [option_name, { default: false, affects: [affects].freeze }]
16
16
  end.freeze
17
17
 
18
- Listener = ->(option_name, boolean) do
18
+ Listener = ->(option_name, bool) do
19
19
  mapping = MAPPING.fetch(option_name)
20
20
 
21
21
  target, name, value = mapping.fetch_values(:target, :name, :value)
22
22
 
23
23
  defined = target.const_defined?(name, false)
24
24
 
25
- boolean ? defined || target.const_set(name, value) : defined && target.send(:remove_const, name)
25
+ bool ? defined || target.const_set(name, value) : defined && target.send(:remove_const, name)
26
26
  end
27
27
 
28
28
  def self.switcher
@@ -30,6 +30,6 @@ class BCDD::Result
30
30
  end
31
31
  end
32
32
 
33
- private_constant :ConstantAlias
33
+ private_constant :ConstantAliases
34
34
  end
35
35
  end