bcdd-result 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +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
|