bcdd-result 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +34 -11
- data/README.md +170 -5
- data/Steepfile +2 -2
- data/lib/bcdd/result/config/switchers/addons.rb +20 -0
- data/lib/bcdd/result/config/{constant_alias.rb → switchers/constant_aliases.rb} +4 -4
- data/lib/bcdd/result/config/switchers/features.rb +28 -0
- data/lib/bcdd/result/config/switchers/pattern_matching.rb +20 -0
- data/lib/bcdd/result/config.rb +8 -28
- data/lib/bcdd/result/context/failure.rb +1 -1
- data/lib/bcdd/result/context/success.rb +2 -2
- data/lib/bcdd/result/context.rb +12 -11
- data/lib/bcdd/result/transitions/tracking/disabled.rb +17 -0
- data/lib/bcdd/result/transitions/tracking/enabled.rb +80 -0
- data/lib/bcdd/result/transitions/tracking.rb +20 -0
- data/lib/bcdd/result/transitions/tree.rb +95 -0
- data/lib/bcdd/result/transitions.rb +30 -0
- data/lib/bcdd/result/version.rb +1 -1
- data/lib/bcdd/result.rb +35 -18
- data/sig/bcdd/result/config.rbs +100 -0
- data/sig/bcdd/result/context.rbs +102 -0
- data/sig/bcdd/result/contract.rbs +119 -0
- data/sig/bcdd/result/data.rbs +16 -0
- data/sig/bcdd/result/error.rbs +31 -0
- data/sig/bcdd/result/expectations.rbs +67 -0
- data/sig/bcdd/result/handler.rbs +47 -0
- data/sig/bcdd/result/mixin.rbs +37 -0
- data/sig/bcdd/result/transitions.rbs +89 -0
- data/sig/bcdd/result/version.rbs +5 -0
- data/sig/bcdd/result.rbs +6 -516
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bf2a03ac7efd3e75e03b2f5617518cb07f40e03cc970c612f3d4dfcdad153ad
|
4
|
+
data.tar.gz: 2717e6e600101401f420164aed020689e9c07dd7f7296c78ec5290562620ca3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1761571f7d0c0a6fd4601a9f8a4c8c18688085226cd061057f95c93737da80ac58e0a99a3803a0e0f93aa5040530975d9b16a7b54df79487ca625fd67d405d6
|
7
|
+
data.tar.gz: 4b22f37d2282de4c0a6e3781e86c9c63457efa83a7d5dc07db4288b9fe19c5b3868cd13a34a1cfc515776cf466e2095bde9d10503f87b8f546ceab9408ef9617
|
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,33 +1,56 @@
|
|
1
1
|
- [\[Unreleased\]](#unreleased)
|
2
|
-
- [\[0.
|
2
|
+
- [\[0.10.0\] - 2023-12-31](#0100---2023-12-31)
|
3
3
|
- [Added](#added)
|
4
|
+
- [\[0.9.1\] - 2023-12-12](#091---2023-12-12)
|
4
5
|
- [Changed](#changed)
|
5
|
-
- [
|
6
|
+
- [Fixed](#fixed)
|
7
|
+
- [\[0.9.0\] - 2023-12-12](#090---2023-12-12)
|
6
8
|
- [Added](#added-1)
|
7
9
|
- [Changed](#changed-1)
|
8
|
-
|
9
|
-
- [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
|
10
|
+
- [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
|
10
11
|
- [Added](#added-2)
|
11
12
|
- [Changed](#changed-2)
|
12
|
-
- [
|
13
|
+
- [Removed](#removed)
|
14
|
+
- [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
|
13
15
|
- [Added](#added-3)
|
14
16
|
- [Changed](#changed-3)
|
15
|
-
- [\[0.
|
17
|
+
- [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
|
16
18
|
- [Added](#added-4)
|
17
|
-
- [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
|
18
|
-
- [Added](#added-5)
|
19
19
|
- [Changed](#changed-4)
|
20
|
+
- [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
|
21
|
+
- [Added](#added-5)
|
22
|
+
- [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
|
23
|
+
- [Added](#added-6)
|
24
|
+
- [Changed](#changed-5)
|
20
25
|
- [Removed](#removed-1)
|
21
26
|
- [\[0.3.0\] - 2023-09-26](#030---2023-09-26)
|
22
|
-
- [Added](#added-6)
|
23
|
-
- [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
|
24
27
|
- [Added](#added-7)
|
28
|
+
- [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
|
29
|
+
- [Added](#added-8)
|
25
30
|
- [Removed](#removed-2)
|
26
31
|
- [\[0.1.0\] - 2023-09-25](#010---2023-09-25)
|
27
|
-
- [Added](#added-
|
32
|
+
- [Added](#added-9)
|
28
33
|
|
29
34
|
## [Unreleased]
|
30
35
|
|
36
|
+
## [0.10.0] - 2023-12-31
|
37
|
+
|
38
|
+
### Added
|
39
|
+
|
40
|
+
- 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.
|
41
|
+
|
42
|
+
- Add `BCDD::Result.config.feature.disable!(:transitions)` and `BCDD::Result.config.feature.enable!(:transitions)` to turn on/off the `BCDD::Result.transitions` feature.
|
43
|
+
|
44
|
+
## [0.9.1] - 2023-12-12
|
45
|
+
|
46
|
+
### Changed
|
47
|
+
|
48
|
+
- **(BREAKING)** Make `BCDD::Result::Context::Success#and_expose()` to produce a halted success by default. You can turn this off by passing `halted: false`.
|
49
|
+
|
50
|
+
### Fixed
|
51
|
+
|
52
|
+
- Make `BCDD::Result::Context#and_then(&block)` accumulate the result value.
|
53
|
+
|
31
54
|
## [0.9.0] - 2023-12-12
|
32
55
|
|
33
56
|
### Added
|
data/README.md
CHANGED
@@ -70,6 +70,8 @@ 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
76
|
- [`config.addon.enable!(:continue)`](#configaddonenablecontinue)
|
75
77
|
- [`config.constant_alias.enable!('Result', 'BCDD::Context')`](#configconstant_aliasenableresult-bcddcontext)
|
@@ -983,7 +985,7 @@ module Divide
|
|
983
985
|
end
|
984
986
|
```
|
985
987
|
|
986
|
-
In the code above, we define a constant `Divide::
|
988
|
+
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
989
|
|
988
990
|
Look what happens if you try to create a result without one of the expected types.
|
989
991
|
|
@@ -1223,7 +1225,7 @@ Divide.call(4, 2)
|
|
1223
1225
|
|
1224
1226
|
#### Value checking - Result Creation
|
1225
1227
|
|
1226
|
-
The `Result::Expectations` supports types of validations. The first is the type checking only, and the second is the type and value checking.
|
1228
|
+
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
1229
|
|
1228
1230
|
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
1231
|
|
@@ -1569,11 +1571,13 @@ Divide.new.call(10, 5)
|
|
1569
1571
|
#<BCDD::Result::Context::Success type=:ok value={:number=>2, :number1=>10, :number2=>5}>
|
1570
1572
|
```
|
1571
1573
|
|
1574
|
+
> 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`.
|
1575
|
+
|
1572
1576
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1573
1577
|
|
1574
1578
|
##### Module example (Singleton Methods)
|
1575
1579
|
|
1576
|
-
|
1580
|
+
`BCDD::Result::Context.mixin` can also produce singleton methods. Below is an example using a module (but it could be a class, too).
|
1577
1581
|
|
1578
1582
|
```ruby
|
1579
1583
|
module Divide
|
@@ -1658,7 +1662,7 @@ Divide.new.call(10, 5)
|
|
1658
1662
|
|
1659
1663
|
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.
|
1660
1664
|
|
1661
|
-
Let's see this using previous example:
|
1665
|
+
Let's see this using the previous example:
|
1662
1666
|
|
1663
1667
|
```ruby
|
1664
1668
|
Divide::Result::Success(:division_completed, number: 2)
|
@@ -1745,6 +1749,160 @@ Divide.call(14, 0)
|
|
1745
1749
|
#<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
|
1746
1750
|
```
|
1747
1751
|
|
1752
|
+
### `BCDD::Result.transitions`
|
1753
|
+
|
1754
|
+
Use `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.
|
1755
|
+
|
1756
|
+
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.
|
1757
|
+
|
1758
|
+
```ruby
|
1759
|
+
class Division
|
1760
|
+
include BCDD::Result.mixin(config: { addon: { continue: true } })
|
1761
|
+
|
1762
|
+
def call(arg1, arg2)
|
1763
|
+
BCDD::Result.transitions(name: 'Division', desc: 'divide two numbers') do
|
1764
|
+
require_numbers(arg1, arg2)
|
1765
|
+
.and_then(:check_for_zeros)
|
1766
|
+
.and_then(:divide)
|
1767
|
+
end
|
1768
|
+
end
|
1769
|
+
|
1770
|
+
private
|
1771
|
+
|
1772
|
+
ValidNumber = ->(arg) { arg.is_a?(Numeric) && arg != Float::NAN && arg != Float::INFINITY }
|
1773
|
+
|
1774
|
+
def require_numbers(arg1, arg2)
|
1775
|
+
ValidNumber[arg1] or return Failure(:invalid_arg, 'arg1 must be a valid number')
|
1776
|
+
ValidNumber[arg2] or return Failure(:invalid_arg, 'arg2 must be a valid number')
|
1777
|
+
|
1778
|
+
Continue([arg1, arg2])
|
1779
|
+
end
|
1780
|
+
|
1781
|
+
def check_for_zeros(numbers)
|
1782
|
+
num1, num2 = numbers
|
1783
|
+
|
1784
|
+
return Failure(:division_by_zero, 'num2 cannot be zero') if num2.zero?
|
1785
|
+
|
1786
|
+
num1.zero? ? Success(:division_completed, 0) : Continue(numbers)
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
def divide((num1, num2))
|
1790
|
+
Success(:division_completed, num1 / num2)
|
1791
|
+
end
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
module SumDivisionsByTwo
|
1795
|
+
extend self, BCDD::Result.mixin
|
1796
|
+
|
1797
|
+
def call(*numbers)
|
1798
|
+
BCDD::Result.transitions(name: 'SumDivisionsByTwo') do
|
1799
|
+
divisions = numbers.map { |number| Division.new.call(number, 2) }
|
1800
|
+
|
1801
|
+
if divisions.any?(&:failure?)
|
1802
|
+
Failure(:errors, divisions.select(&:failure?).map(&:value))
|
1803
|
+
else
|
1804
|
+
Success(:sum, divisions.sum(&:value))
|
1805
|
+
end
|
1806
|
+
end
|
1807
|
+
end
|
1808
|
+
end
|
1809
|
+
```
|
1810
|
+
|
1811
|
+
Let's see the result of the `SumDivisionsByTwo` call:
|
1812
|
+
|
1813
|
+
```ruby
|
1814
|
+
result = SumDivisionsByTwo.call(20, 10)
|
1815
|
+
# => #<BCDD::Result::Success type=:sum value=15>
|
1816
|
+
|
1817
|
+
result.transitions
|
1818
|
+
{
|
1819
|
+
:version =>1,
|
1820
|
+
:metadata => {
|
1821
|
+
:duration => 0, # milliseconds
|
1822
|
+
:tree_map => [0, [[1, []], [2, []]]], # represents the tree of transitions using the id of each transition block
|
1823
|
+
},
|
1824
|
+
:records => [
|
1825
|
+
{
|
1826
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1827
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1828
|
+
:current => {:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1829
|
+
:result => {:kind=>:success, :type=>:continued, :value=>[20, 2]},
|
1830
|
+
:and_then => {},
|
1831
|
+
:time => 2023-12-31 22:19:33.281619 UTC
|
1832
|
+
},
|
1833
|
+
{
|
1834
|
+
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1835
|
+
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1836
|
+
:current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1837
|
+
:result=>{:kind=>:success, :type=>:continued, :value=>[20, 2]},
|
1838
|
+
:and_then=>{:type=>:method, :arg=>nil, :subject=>#<Division:0x0000000103e5f7c8>, :method_name=>:check_for_zeros},
|
1839
|
+
:time=>2023-12-31 22:19:33.281693 UTC
|
1840
|
+
},
|
1841
|
+
{
|
1842
|
+
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1843
|
+
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1844
|
+
:current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1845
|
+
:result=>{:kind=>:success, :type=>:division_completed, :value=>10},
|
1846
|
+
:and_then=>{:type=>:method, :arg=>nil, :subject=>#<Division:0x0000000103e5f7c8>, :method_name=>:divide},
|
1847
|
+
:time=>2023-12-31 22:19:33.281715 UTC
|
1848
|
+
},
|
1849
|
+
{
|
1850
|
+
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1851
|
+
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1852
|
+
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1853
|
+
:result=>{:kind=>:success, :type=>:continued, :value=>[10, 2]},
|
1854
|
+
:and_then=>{},
|
1855
|
+
:time=>2023-12-31 22:19:33.281747 UTC
|
1856
|
+
},
|
1857
|
+
{
|
1858
|
+
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1859
|
+
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1860
|
+
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1861
|
+
:result=>{:kind=>:success, :type=>:continued, :value=>[10, 2]},
|
1862
|
+
:and_then=>{:type=>:method, :arg=>nil, :subject=>#<Division:0x0000000103e5f1b0>, :method_name=>:check_for_zeros},
|
1863
|
+
:time=>2023-12-31 22:19:33.281755 UTC
|
1864
|
+
},
|
1865
|
+
{
|
1866
|
+
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1867
|
+
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1868
|
+
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1869
|
+
:result=>{:kind=>:success, :type=>:division_completed, :value=>5},
|
1870
|
+
:and_then=>{:type=>:method, :arg=>nil, :subject=>#<Division:0x0000000103e5f1b0>, :method_name=>:divide},
|
1871
|
+
:time=>2023-12-31 22:19:33.281763 UTC
|
1872
|
+
},
|
1873
|
+
{
|
1874
|
+
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1875
|
+
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1876
|
+
:current=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1877
|
+
:result=>{:kind=>:success, :type=>:sum, :value=>15},
|
1878
|
+
:and_then=>{},
|
1879
|
+
:time=>2023-12-31 22:19:33.281784 UTC
|
1880
|
+
}
|
1881
|
+
]
|
1882
|
+
}
|
1883
|
+
```
|
1884
|
+
|
1885
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1886
|
+
|
1887
|
+
#### Configuration
|
1888
|
+
|
1889
|
+
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.
|
1890
|
+
|
1891
|
+
```ruby
|
1892
|
+
BCDD::Result.configuration do |config|
|
1893
|
+
config.feature.disable!(:transitions)
|
1894
|
+
end
|
1895
|
+
|
1896
|
+
result = SumDivisionsByTwo.call(20, 10)
|
1897
|
+
# => #<BCDD::Result::Success type=:sum value=15>
|
1898
|
+
|
1899
|
+
result.transitions
|
1900
|
+
|
1901
|
+
{:version=>1, :records=>[], :metadata=>{:duration=>0, :tree_map=>[]}}
|
1902
|
+
```
|
1903
|
+
|
1904
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1905
|
+
|
1748
1906
|
### `BCDD::Result.configuration`
|
1749
1907
|
|
1750
1908
|
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.
|
@@ -1791,7 +1949,7 @@ PS: I'm using `::Rails.env.production?` to check the environment, but you can us
|
|
1791
1949
|
|
1792
1950
|
### `BCDD::Result.config`
|
1793
1951
|
|
1794
|
-
The `BCDD::Result.config` allows you to access the current configuration.
|
1952
|
+
The `BCDD::Result.config` allows you to access the current configuration.
|
1795
1953
|
|
1796
1954
|
**BCDD::Result.config.addon**
|
1797
1955
|
|
@@ -1857,6 +2015,13 @@ BCDD::Result.config.feature.options
|
|
1857
2015
|
# "BCDD::Result::Expectations,
|
1858
2016
|
# "BCDD::Result::Context::Expectations"
|
1859
2017
|
# ]
|
2018
|
+
# },
|
2019
|
+
# :transitions=>{
|
2020
|
+
# :enabled=>true,
|
2021
|
+
# :affects=>[
|
2022
|
+
# "BCDD::Result",
|
2023
|
+
# "BCDD::Result::Context"
|
2024
|
+
# ]
|
1860
2025
|
# }
|
1861
2026
|
# }
|
1862
2027
|
```
|
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'
|
14
|
-
# library 'strong_json'
|
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,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module Addons
|
6
|
+
OPTIONS = {
|
7
|
+
continue: {
|
8
|
+
default: false,
|
9
|
+
affects: %w[BCDD::Result BCDD::Result::Context BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
10
|
+
}
|
11
|
+
}.transform_values!(&:freeze).freeze
|
12
|
+
|
13
|
+
def self.switcher
|
14
|
+
Switcher.new(options: OPTIONS)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private_constant :Addons
|
19
|
+
end
|
20
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class BCDD::Result
|
4
4
|
class Config
|
5
|
-
module
|
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,
|
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
|
-
|
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 :
|
33
|
+
private_constant :ConstantAliases
|
34
34
|
end
|
35
35
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module Features
|
6
|
+
OPTIONS = {
|
7
|
+
expectations: {
|
8
|
+
default: true,
|
9
|
+
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
10
|
+
},
|
11
|
+
transitions: {
|
12
|
+
default: true,
|
13
|
+
affects: %w[BCDD::Result BCDD::Result::Context]
|
14
|
+
}
|
15
|
+
}.transform_values!(&:freeze).freeze
|
16
|
+
|
17
|
+
Listener = ->(option_name, _bool) do
|
18
|
+
Thread.current[Transitions::THREAD_VAR_NAME] = nil if option_name == :transitions
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.switcher
|
22
|
+
Switcher.new(options: OPTIONS, listener: Listener)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private_constant :Features
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module PatternMatching
|
6
|
+
OPTIONS = {
|
7
|
+
nil_as_valid_value_checking: {
|
8
|
+
default: false,
|
9
|
+
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
10
|
+
}
|
11
|
+
}.transform_values!(&:freeze).freeze
|
12
|
+
|
13
|
+
def self.switcher
|
14
|
+
Switcher.new(options: OPTIONS)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private_constant :PatternMatching
|
19
|
+
end
|
20
|
+
end
|
data/lib/bcdd/result/config.rb
CHANGED
@@ -4,40 +4,22 @@ require 'singleton'
|
|
4
4
|
|
5
5
|
require_relative 'config/options'
|
6
6
|
require_relative 'config/switcher'
|
7
|
-
require_relative 'config/
|
7
|
+
require_relative 'config/switchers/addons'
|
8
|
+
require_relative 'config/switchers/constant_aliases'
|
9
|
+
require_relative 'config/switchers/features'
|
10
|
+
require_relative 'config/switchers/pattern_matching'
|
8
11
|
|
9
12
|
class BCDD::Result
|
10
13
|
class Config
|
11
14
|
include Singleton
|
12
15
|
|
13
|
-
ADDON = {
|
14
|
-
continue: {
|
15
|
-
default: false,
|
16
|
-
affects: %w[BCDD::Result BCDD::Result::Context BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
17
|
-
}
|
18
|
-
}.transform_values!(&:freeze).freeze
|
19
|
-
|
20
|
-
FEATURE = {
|
21
|
-
expectations: {
|
22
|
-
default: true,
|
23
|
-
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
24
|
-
}
|
25
|
-
}.transform_values!(&:freeze).freeze
|
26
|
-
|
27
|
-
PATTERN_MATCHING = {
|
28
|
-
nil_as_valid_value_checking: {
|
29
|
-
default: false,
|
30
|
-
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
31
|
-
}
|
32
|
-
}.transform_values!(&:freeze).freeze
|
33
|
-
|
34
16
|
attr_reader :addon, :feature, :constant_alias, :pattern_matching
|
35
17
|
|
36
18
|
def initialize
|
37
|
-
@addon =
|
38
|
-
@feature =
|
39
|
-
@constant_alias =
|
40
|
-
@pattern_matching =
|
19
|
+
@addon = Addons.switcher
|
20
|
+
@feature = Features.switcher
|
21
|
+
@constant_alias = ConstantAliases.switcher
|
22
|
+
@pattern_matching = PatternMatching.switcher
|
41
23
|
end
|
42
24
|
|
43
25
|
def freeze
|
@@ -65,7 +47,5 @@ class BCDD::Result
|
|
65
47
|
def inspect
|
66
48
|
"#<#{self.class.name} options=#{options.keys.sort.inspect}>"
|
67
49
|
end
|
68
|
-
|
69
|
-
private_constant :ADDON, :FEATURE, :PATTERN_MATCHING
|
70
50
|
end
|
71
51
|
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
class BCDD::Result::Context::Success < BCDD::Result::Context
|
4
4
|
include ::BCDD::Result::Success::Methods
|
5
5
|
|
6
|
-
def and_expose(type, keys)
|
6
|
+
def and_expose(type, keys, halted: true)
|
7
7
|
unless keys.is_a?(::Array) && !keys.empty? && keys.all?(::Symbol)
|
8
8
|
raise ::ArgumentError, 'keys must be an Array of Symbols'
|
9
9
|
end
|
10
10
|
|
11
11
|
exposed_value = acc.merge(value).slice(*keys)
|
12
12
|
|
13
|
-
self.class.new(type: type, value: exposed_value, subject: subject)
|
13
|
+
self.class.new(type: type, value: exposed_value, subject: subject, halted: halted)
|
14
14
|
end
|
15
15
|
end
|
data/lib/bcdd/result/context.rb
CHANGED
@@ -40,19 +40,20 @@ class BCDD::Result
|
|
40
40
|
-1
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def call_and_then_subject_method!(method, context_data)
|
44
|
+
acc.merge!(value.merge(context_data))
|
45
|
+
|
46
|
+
case SubjectMethodArity[method]
|
47
|
+
when 0 then subject.send(method.name)
|
48
|
+
when 1 then subject.send(method.name, **acc)
|
49
|
+
else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 1)
|
50
|
+
end
|
51
|
+
end
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
when 0 then subject.send(method_name)
|
51
|
-
when 1 then subject.send(method_name, **acc)
|
52
|
-
else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 1)
|
53
|
-
end
|
53
|
+
def call_and_then_block!(block)
|
54
|
+
acc.merge!(value)
|
54
55
|
|
55
|
-
|
56
|
+
block.call(acc)
|
56
57
|
end
|
57
58
|
|
58
59
|
def ensure_result_object(result, origin:)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BCDD::Result::Transitions
|
4
|
+
module Tracking::Disabled
|
5
|
+
def self.start(name:, desc:); end
|
6
|
+
|
7
|
+
def self.finish(result:); end
|
8
|
+
|
9
|
+
def self.reset!; end
|
10
|
+
|
11
|
+
def self.record(result); end
|
12
|
+
|
13
|
+
def self.record_and_then(_type, _data, _subject)
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BCDD::Result::Transitions
|
4
|
+
class Tracking::Enabled
|
5
|
+
attr_accessor :tree, :records, :root_started_at
|
6
|
+
|
7
|
+
private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=
|
8
|
+
|
9
|
+
def start(name:, desc:)
|
10
|
+
name_and_desc = [name, desc]
|
11
|
+
|
12
|
+
tree.frozen? ? root_start(name_and_desc) : tree.insert!(name_and_desc)
|
13
|
+
end
|
14
|
+
|
15
|
+
def finish(result:)
|
16
|
+
node = tree.current
|
17
|
+
|
18
|
+
tree.move_up!
|
19
|
+
|
20
|
+
return unless node.root?
|
21
|
+
|
22
|
+
duration = (now_in_milliseconds - root_started_at)
|
23
|
+
|
24
|
+
metadata = { duration: duration, tree_map: tree.nested_ids }
|
25
|
+
|
26
|
+
result.send(:transitions=, version: Tracking::VERSION, records: records, metadata: metadata)
|
27
|
+
|
28
|
+
reset!
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset!
|
32
|
+
self.tree = Tracking::EMPTY_TREE
|
33
|
+
end
|
34
|
+
|
35
|
+
def record(result)
|
36
|
+
return if tree.frozen?
|
37
|
+
|
38
|
+
track(result, time: ::Time.now.getutc)
|
39
|
+
end
|
40
|
+
|
41
|
+
def record_and_then(type_arg, arg, subject)
|
42
|
+
type = type_arg.instance_of?(::Method) ? :method : type_arg
|
43
|
+
|
44
|
+
unless tree.frozen?
|
45
|
+
current_and_then = { type: type, arg: arg, subject: subject }
|
46
|
+
current_and_then[:method_name] = type_arg.name if type == :method
|
47
|
+
|
48
|
+
tree.current.value[1] = current_and_then
|
49
|
+
end
|
50
|
+
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
TreeNodeValueNormalizer = ->(id, (nam, des)) { [{ id: id, name: nam, desc: des }, Tracking::EMPTY_HASH] }
|
57
|
+
|
58
|
+
def root_start(name_and_desc)
|
59
|
+
self.root_started_at = now_in_milliseconds
|
60
|
+
|
61
|
+
self.records = []
|
62
|
+
|
63
|
+
self.tree = Tree.new(name_and_desc, normalizer: TreeNodeValueNormalizer)
|
64
|
+
end
|
65
|
+
|
66
|
+
def track(result, time:)
|
67
|
+
result = result.data.to_h
|
68
|
+
|
69
|
+
root, = tree.root_value
|
70
|
+
parent, = tree.parent_value
|
71
|
+
current, and_then = tree.current_value
|
72
|
+
|
73
|
+
records << { root: root, parent: parent, current: current, result: result, and_then: and_then, time: time }
|
74
|
+
end
|
75
|
+
|
76
|
+
def now_in_milliseconds
|
77
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|