bcdd-result 0.9.1 → 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 +19 -9
- data/README.md +168 -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.rb +10 -15
- 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 +27 -16
- 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 +4 -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,36 +1,46 @@
|
|
1
1
|
- [\[Unreleased\]](#unreleased)
|
2
|
+
- [\[0.10.0\] - 2023-12-31](#0100---2023-12-31)
|
3
|
+
- [Added](#added)
|
2
4
|
- [\[0.9.1\] - 2023-12-12](#091---2023-12-12)
|
3
5
|
- [Changed](#changed)
|
4
6
|
- [Fixed](#fixed)
|
5
7
|
- [\[0.9.0\] - 2023-12-12](#090---2023-12-12)
|
6
|
-
- [Added](#added)
|
8
|
+
- [Added](#added-1)
|
7
9
|
- [Changed](#changed-1)
|
8
10
|
- [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
|
9
|
-
- [Added](#added-
|
11
|
+
- [Added](#added-2)
|
10
12
|
- [Changed](#changed-2)
|
11
13
|
- [Removed](#removed)
|
12
14
|
- [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
|
13
|
-
- [Added](#added-
|
15
|
+
- [Added](#added-3)
|
14
16
|
- [Changed](#changed-3)
|
15
17
|
- [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
|
16
|
-
- [Added](#added-
|
18
|
+
- [Added](#added-4)
|
17
19
|
- [Changed](#changed-4)
|
18
20
|
- [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
|
19
|
-
- [Added](#added-4)
|
20
|
-
- [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
|
21
21
|
- [Added](#added-5)
|
22
|
+
- [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
|
23
|
+
- [Added](#added-6)
|
22
24
|
- [Changed](#changed-5)
|
23
25
|
- [Removed](#removed-1)
|
24
26
|
- [\[0.3.0\] - 2023-09-26](#030---2023-09-26)
|
25
|
-
- [Added](#added-6)
|
26
|
-
- [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
|
27
27
|
- [Added](#added-7)
|
28
|
+
- [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
|
29
|
+
- [Added](#added-8)
|
28
30
|
- [Removed](#removed-2)
|
29
31
|
- [\[0.1.0\] - 2023-09-25](#010---2023-09-25)
|
30
|
-
- [Added](#added-
|
32
|
+
- [Added](#added-9)
|
31
33
|
|
32
34
|
## [Unreleased]
|
33
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
|
+
|
34
44
|
## [0.9.1] - 2023-12-12
|
35
45
|
|
36
46
|
### Changed
|
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
|
|
@@ -1575,7 +1577,7 @@ Divide.new.call(10, 5)
|
|
1575
1577
|
|
1576
1578
|
##### Module example (Singleton Methods)
|
1577
1579
|
|
1578
|
-
|
1580
|
+
`BCDD::Result::Context.mixin` can also produce singleton methods. Below is an example using a module (but it could be a class, too).
|
1579
1581
|
|
1580
1582
|
```ruby
|
1581
1583
|
module Divide
|
@@ -1660,7 +1662,7 @@ Divide.new.call(10, 5)
|
|
1660
1662
|
|
1661
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.
|
1662
1664
|
|
1663
|
-
Let's see this using previous example:
|
1665
|
+
Let's see this using the previous example:
|
1664
1666
|
|
1665
1667
|
```ruby
|
1666
1668
|
Divide::Result::Success(:division_completed, number: 2)
|
@@ -1747,6 +1749,160 @@ Divide.call(14, 0)
|
|
1747
1749
|
#<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
|
1748
1750
|
```
|
1749
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
|
+
|
1750
1906
|
### `BCDD::Result.configuration`
|
1751
1907
|
|
1752
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.
|
@@ -1793,7 +1949,7 @@ PS: I'm using `::Rails.env.production?` to check the environment, but you can us
|
|
1793
1949
|
|
1794
1950
|
### `BCDD::Result.config`
|
1795
1951
|
|
1796
|
-
The `BCDD::Result.config` allows you to access the current configuration.
|
1952
|
+
The `BCDD::Result.config` allows you to access the current configuration.
|
1797
1953
|
|
1798
1954
|
**BCDD::Result.config.addon**
|
1799
1955
|
|
@@ -1859,6 +2015,13 @@ BCDD::Result.config.feature.options
|
|
1859
2015
|
# "BCDD::Result::Expectations,
|
1860
2016
|
# "BCDD::Result::Context::Expectations"
|
1861
2017
|
# ]
|
2018
|
+
# },
|
2019
|
+
# :transitions=>{
|
2020
|
+
# :enabled=>true,
|
2021
|
+
# :affects=>[
|
2022
|
+
# "BCDD::Result",
|
2023
|
+
# "BCDD::Result::Context"
|
2024
|
+
# ]
|
1862
2025
|
# }
|
1863
2026
|
# }
|
1864
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
|
data/lib/bcdd/result/context.rb
CHANGED
@@ -40,25 +40,20 @@ class BCDD::Result
|
|
40
40
|
-1
|
41
41
|
end
|
42
42
|
|
43
|
-
def call_and_then_subject_method(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
when 1 then subject.send(method_name, **acc)
|
52
|
-
else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 1)
|
53
|
-
end
|
54
|
-
|
55
|
-
ensure_result_object(result, origin: :method)
|
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
|
56
51
|
end
|
57
52
|
|
58
|
-
def call_and_then_block(block)
|
53
|
+
def call_and_then_block!(block)
|
59
54
|
acc.merge!(value)
|
60
55
|
|
61
|
-
|
56
|
+
block.call(acc)
|
62
57
|
end
|
63
58
|
|
64
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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
module Transitions
|
5
|
+
module Tracking
|
6
|
+
require_relative 'tracking/enabled'
|
7
|
+
require_relative 'tracking/disabled'
|
8
|
+
|
9
|
+
EMPTY_ARRAY = [].freeze
|
10
|
+
EMPTY_HASH = {}.freeze
|
11
|
+
EMPTY_TREE = Tree.new(nil).freeze
|
12
|
+
VERSION = 1
|
13
|
+
EMPTY = { version: VERSION, records: EMPTY_ARRAY, metadata: { duration: 0, tree_map: EMPTY_ARRAY } }.freeze
|
14
|
+
|
15
|
+
def self.instance
|
16
|
+
Config.instance.feature.enabled?(:transitions) ? Tracking::Enabled.new : Tracking::Disabled
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|