bcdd-result 0.12.0 → 0.13.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 +16 -1
- data/CHANGELOG.md +70 -16
- data/README.md +293 -83
- data/Steepfile +4 -4
- data/examples/multiple_listeners/Rakefile +55 -0
- data/examples/multiple_listeners/app/models/account/member.rb +10 -0
- data/examples/multiple_listeners/app/models/account/owner_creation.rb +62 -0
- data/examples/multiple_listeners/app/models/account.rb +11 -0
- data/examples/multiple_listeners/app/models/user/creation.rb +67 -0
- data/examples/multiple_listeners/app/models/user/token/creation.rb +51 -0
- data/examples/multiple_listeners/app/models/user/token.rb +7 -0
- data/examples/multiple_listeners/app/models/user.rb +15 -0
- data/examples/multiple_listeners/config/boot.rb +16 -0
- data/examples/multiple_listeners/config/initializers/bcdd.rb +11 -0
- data/examples/multiple_listeners/config.rb +27 -0
- data/examples/multiple_listeners/db/setup.rb +61 -0
- data/examples/multiple_listeners/lib/bcdd/result/rollback_on_failure.rb +15 -0
- data/examples/multiple_listeners/lib/bcdd/result/transitions_record.rb +28 -0
- data/examples/multiple_listeners/lib/runtime_breaker.rb +11 -0
- data/examples/multiple_listeners/lib/transitions_listener/stdout.rb +54 -0
- data/examples/single_listener/Rakefile +92 -0
- data/examples/single_listener/app/models/account/member.rb +10 -0
- data/examples/single_listener/app/models/account/owner_creation.rb +62 -0
- data/examples/single_listener/app/models/account.rb +11 -0
- data/examples/single_listener/app/models/user/creation.rb +67 -0
- data/examples/single_listener/app/models/user/token/creation.rb +51 -0
- data/examples/single_listener/app/models/user/token.rb +7 -0
- data/examples/single_listener/app/models/user.rb +15 -0
- data/examples/single_listener/config/boot.rb +16 -0
- data/examples/single_listener/config/initializers/bcdd.rb +11 -0
- data/examples/single_listener/config.rb +23 -0
- data/examples/single_listener/db/setup.rb +49 -0
- data/examples/single_listener/lib/bcdd/result/rollback_on_failure.rb +15 -0
- data/examples/single_listener/lib/runtime_breaker.rb +11 -0
- data/examples/single_listener/lib/single_transitions_listener.rb +108 -0
- data/lib/bcdd/result/callable_and_then/caller.rb +1 -1
- data/lib/bcdd/result/config.rb +6 -1
- data/lib/bcdd/result/context/expectations/mixin.rb +2 -2
- data/lib/bcdd/result/context/mixin.rb +2 -2
- data/lib/bcdd/result/context/success.rb +20 -2
- data/lib/bcdd/result/contract/for_types.rb +1 -1
- data/lib/bcdd/result/contract/for_types_and_values.rb +2 -0
- data/lib/bcdd/result/expectations/mixin.rb +2 -2
- data/lib/bcdd/result/ignored_types.rb +14 -0
- data/lib/bcdd/result/mixin.rb +2 -2
- data/lib/bcdd/result/transitions/config.rb +26 -0
- data/lib/bcdd/result/transitions/listener.rb +51 -0
- data/lib/bcdd/result/transitions/listeners.rb +87 -0
- data/lib/bcdd/result/transitions/tracking/disabled.rb +1 -13
- data/lib/bcdd/result/transitions/tracking/enabled.rb +76 -17
- data/lib/bcdd/result/transitions/tracking.rb +8 -3
- data/lib/bcdd/result/transitions/tree.rb +26 -0
- data/lib/bcdd/result/transitions.rb +3 -4
- data/lib/bcdd/result/version.rb +1 -1
- data/lib/bcdd/result.rb +7 -5
- data/sig/bcdd/result/config.rbs +1 -0
- data/sig/bcdd/result/context.rbs +9 -0
- data/sig/bcdd/result/ignored_types.rbs +9 -0
- data/sig/bcdd/result/transitions.rbs +96 -7
- data/sig/bcdd/result.rbs +2 -2
- metadata +42 -6
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
<p align="center">
|
2
2
|
<h1 align="center" id="-bcddresult">🔀 BCDD::Result</h1>
|
3
|
-
<p align="center"><i>
|
3
|
+
<p align="center"><i>Unleash a pragmatic and observable use of Result Pattern and Railway-Oriented Programming in Ruby.</i></p>
|
4
4
|
<p align="center">
|
5
5
|
<img src="https://img.shields.io/badge/ruby->%3D%202.7.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
|
6
6
|
<a href="https://rubygems.org/gems/bcdd-result"><img src="https://badge.fury.io/rb/bcdd-result.svg" alt="bcdd-result gem version" height="18"></a>
|
7
|
+
<a href="https://codeclimate.com/github/B-CDD/result/maintainability"><img src="https://api.codeclimate.com/v1/badges/aa8360f8f012d7dedd62/maintainability" /></a>
|
8
|
+
<a href="https://codeclimate.com/github/B-CDD/result/test_coverage"><img src="https://api.codeclimate.com/v1/badges/aa8360f8f012d7dedd62/test_coverage" /></a>
|
7
9
|
</p>
|
8
10
|
</p>
|
9
11
|
|
@@ -70,13 +72,14 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
|
|
70
72
|
- [Module example (Singleton Methods)](#module-example-singleton-methods-1)
|
71
73
|
- [`BCDD::Result::Context::Expectations`](#bcddresultcontextexpectations)
|
72
74
|
- [Mixin add-ons](#mixin-add-ons)
|
73
|
-
|
74
|
-
|
75
|
-
- [
|
76
|
-
- [
|
77
|
-
- [`
|
78
|
-
- [`
|
79
|
-
- [`
|
75
|
+
- [`BCDD::Result.transitions`](#bcddresulttransitions)
|
76
|
+
- [`ids_tree` *versus* `ids_matrix`](#ids_tree-versus-ids_matrix)
|
77
|
+
- [Configuration](#configuration)
|
78
|
+
- [Turning on/off](#turning-onoff)
|
79
|
+
- [Setting a `trace_id` fetcher](#setting-a-trace_id-fetcher)
|
80
|
+
- [Setting a `listener`](#setting-a-listener)
|
81
|
+
- [Setting multiple `listeners`](#setting-multiple-listeners)
|
82
|
+
- [`BCDD::Result.configuration`](#bcddresultconfiguration)
|
80
83
|
- [`BCDD::Result.config`](#bcddresultconfig)
|
81
84
|
- [`BCDD::Result#and_then!`](#bcddresultand_then)
|
82
85
|
- [Dependency Injection](#dependency-injection-1)
|
@@ -1418,7 +1421,7 @@ result = Divide.new.call(4, 2)
|
|
1418
1421
|
|
1419
1422
|
# The example below shows an error because the :ok type is not allowed.
|
1420
1423
|
# But look at the allowed types have only one type (:division_completed).
|
1421
|
-
# This is because the :
|
1424
|
+
# This is because the :_continue_ type is ignored by the expectations.
|
1422
1425
|
#
|
1423
1426
|
result.success?(:ok)
|
1424
1427
|
# type :ok is not allowed. Allowed types: :division_completed (BCDD::Result::Contract::Error::UnexpectedType)
|
@@ -1783,7 +1786,9 @@ Division.call(14, 0)
|
|
1783
1786
|
#<BCDD::Result::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
|
1784
1787
|
```
|
1785
1788
|
|
1786
|
-
|
1789
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1790
|
+
|
1791
|
+
## `BCDD::Result.transitions`
|
1787
1792
|
|
1788
1793
|
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.
|
1789
1794
|
|
@@ -1804,7 +1809,7 @@ class Division
|
|
1804
1809
|
|
1805
1810
|
private
|
1806
1811
|
|
1807
|
-
ValidNumber = ->(arg) { arg.is_a?(Numeric) && arg
|
1812
|
+
ValidNumber = ->(arg) { arg.is_a?(Numeric) && (!arg.respond_to?(:finite?) || arg.finite?) }
|
1808
1813
|
|
1809
1814
|
def require_numbers((arg1, arg2))
|
1810
1815
|
ValidNumber[arg1] or return Failure(:invalid_arg, 'arg1 must be a valid number')
|
@@ -1851,83 +1856,85 @@ result = SumDivisionsByTwo.call(20, 10)
|
|
1851
1856
|
|
1852
1857
|
result.transitions
|
1853
1858
|
{
|
1854
|
-
:version =>1,
|
1859
|
+
:version => 1,
|
1855
1860
|
:metadata => {
|
1856
|
-
:duration => 0,
|
1857
|
-
:
|
1861
|
+
:duration => 0, # milliseconds
|
1862
|
+
:trace_id => nil, # can be set through configuration
|
1863
|
+
:ids_tree => [0, [[1, []], [2, []]]],
|
1864
|
+
:ids_matrix => {0 => [0, 0], 1 => [1, 1], 2 => [2, 1]}
|
1858
1865
|
},
|
1859
|
-
:records
|
1866
|
+
:records=> [
|
1860
1867
|
{
|
1861
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1862
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1863
|
-
:current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1864
|
-
:result=>{:kind=>:success, :type=>:
|
1865
|
-
:and_then=>{},
|
1866
|
-
:time=>2024-01-02
|
1868
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1869
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1870
|
+
:current => {:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1871
|
+
:result => {:kind=>:success, :type=>:_given_, :value=>[20, 2], :source=><Division:0x0000000102fd7ed0>},
|
1872
|
+
:and_then => {},
|
1873
|
+
:time => 2024-01-26 02:53:11.310346 UTC
|
1867
1874
|
},
|
1868
1875
|
{
|
1869
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1870
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1871
|
-
:current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1872
|
-
:result=>{:kind=>:success, :type=>:
|
1873
|
-
:and_then=>{:type=>:method, :arg=>nil, :
|
1874
|
-
:time=>2024-01-02
|
1876
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1877
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1878
|
+
:current => {:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1879
|
+
:result => {:kind=>:success, :type=>:_continue_, :value=>[20, 2], :source=><Division:0x0000000102fd7ed0>},
|
1880
|
+
:and_then => {:type=>:method, :arg=>nil, :method_name=>:require_numbers},
|
1881
|
+
:time => 2024-01-26 02:53:11.310392 UTC
|
1875
1882
|
},
|
1876
1883
|
{
|
1877
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1878
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1879
|
-
:current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1880
|
-
:result=>{:kind=>:success, :type=>:
|
1881
|
-
:and_then=>{:type=>:method, :arg=>nil, :
|
1882
|
-
:time=>2024-01-02
|
1884
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1885
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1886
|
+
:current => {:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1887
|
+
:result => {:kind=>:success, :type=>:_continue_, :value=>[20, 2], :source=><Division:0x0000000102fd7ed0>},
|
1888
|
+
:and_then => {:type=>:method, :arg=>nil, :method_name=>:check_for_zeros},
|
1889
|
+
:time=>2024-01-26 02:53:11.310403 UTC
|
1883
1890
|
},
|
1884
1891
|
{
|
1885
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1886
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1887
|
-
:current=>{:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1888
|
-
:result=>{:kind=>:success, :type=>:division_completed, :value=>10},
|
1889
|
-
:and_then=>{:type=>:method, :arg=>nil, :
|
1890
|
-
:time=>2024-01-02
|
1892
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1893
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1894
|
+
:current => {:id=>1, :name=>"Division", :desc=>"divide two numbers"},
|
1895
|
+
:result => {:kind=>:success, :type=>:division_completed, :value=>10, :source=><Division:0x0000000102fd7ed0>},
|
1896
|
+
:and_then => {:type=>:method, :arg=>nil, :method_name=>:divide},
|
1897
|
+
:time => 2024-01-26 02:53:11.310409 UTC
|
1891
1898
|
},
|
1892
1899
|
{
|
1893
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1894
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1895
|
-
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1896
|
-
:result=>{:kind=>:success, :type=>:
|
1897
|
-
:and_then=>{},
|
1898
|
-
:time=>2024-01-02
|
1900
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1901
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1902
|
+
:current => {:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1903
|
+
:result => {:kind=>:success, :type=>:_given_, :value=>[10, 2], :source=><Division:0x0000000102fd6378>},
|
1904
|
+
:and_then => {},
|
1905
|
+
:time => 2024-01-26 02:53:11.310424 UTC
|
1899
1906
|
},
|
1900
1907
|
{
|
1901
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1902
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1903
|
-
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1904
|
-
:result=>{:kind=>:success, :type=>:
|
1905
|
-
:and_then=>{:type=>:method, :arg=>nil, :
|
1906
|
-
:time=>2024-01-02
|
1908
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1909
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1910
|
+
:current => {:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1911
|
+
:result => {:kind=>:success, :type=>:_continue_, :value=>[10, 2], :source=><Division:0x0000000102fd6378>},
|
1912
|
+
:and_then => {:type=>:method, :arg=>nil, :method_name=>:require_numbers},
|
1913
|
+
:time => 2024-01-26 02:53:11.310428 UTC
|
1907
1914
|
},
|
1908
1915
|
{
|
1909
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1910
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1911
|
-
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1912
|
-
:result=>{:kind=>:success, :type=>:
|
1913
|
-
:and_then=>{:type=>:method, :arg=>nil, :
|
1914
|
-
:time=>2024-01-02
|
1916
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1917
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1918
|
+
:current => {:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1919
|
+
:result => {:kind=>:success, :type=>:_continue_, :value=>[10, 2], :source=><Division:0x0000000102fd6378>},
|
1920
|
+
:and_then => {:type=>:method, :arg=>nil, :method_name=>:check_for_zeros},
|
1921
|
+
:time => 2024-01-26 02:53:11.310431 UTC
|
1915
1922
|
},
|
1916
1923
|
{
|
1917
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1918
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1919
|
-
:current=>{:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1920
|
-
:result=>{:kind=>:success, :type=>:division_completed, :value=>5},
|
1921
|
-
:and_then=>{:type=>:method, :arg=>nil, :
|
1922
|
-
:time=>2024-01-02
|
1924
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1925
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1926
|
+
:current => {:id=>2, :name=>"Division", :desc=>"divide two numbers"},
|
1927
|
+
:result => {:kind=>:success, :type=>:division_completed, :value=>5, :source=><Division:0x0000000102fd6378>},
|
1928
|
+
:and_then => {:type=>:method, :arg=>nil, :method_name=>:divide},
|
1929
|
+
:time => 2024-01-26 02:53:11.310434 UTC
|
1923
1930
|
},
|
1924
1931
|
{
|
1925
|
-
:root=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1926
|
-
:parent=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1927
|
-
:current=>{:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1928
|
-
:result=>{:kind=>:success, :type=>:sum, :value=>15},
|
1929
|
-
:and_then=>{},
|
1930
|
-
:time=>2024-01-02
|
1932
|
+
:root => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1933
|
+
:parent => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1934
|
+
:current => {:id=>0, :name=>"SumDivisionsByTwo", :desc=>nil},
|
1935
|
+
:result => {:kind=>:success, :type=>:sum, :value=>15, :source=>SumDivisionsByTwo},
|
1936
|
+
:and_then => {},
|
1937
|
+
:time => 2024-01-26 02:53:11.310444 UTC
|
1931
1938
|
}
|
1932
1939
|
]
|
1933
1940
|
}
|
@@ -1935,7 +1942,46 @@ result.transitions
|
|
1935
1942
|
|
1936
1943
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1937
1944
|
|
1938
|
-
|
1945
|
+
### `ids_tree` *versus* `ids_matrix`
|
1946
|
+
|
1947
|
+
The `:ids_matrix`. It is a simplification of the `:ids_tree` property (a graph/tree representation of the transitions ids).
|
1948
|
+
|
1949
|
+
The matrix rows are the direct transitions from the root transition block, and the columns are the transitions nested from the direct transitions.
|
1950
|
+
|
1951
|
+
Use these data structures to build your own visualization of the transitions.
|
1952
|
+
|
1953
|
+
> Check out [Transitions Listener example](examples/single_listener/lib/single_transitions_listener.rb) to see how a listener can be used to build a visualization of the transitions, using these properties.
|
1954
|
+
|
1955
|
+
```ruby
|
1956
|
+
# ids_tree #
|
1957
|
+
0 # [0, [
|
1958
|
+
|- 1 # [1, [[2, []]]],
|
1959
|
+
| |- 2 # [3, []],
|
1960
|
+
|- 3 # [4, [
|
1961
|
+
|- 4 # [5, []],
|
1962
|
+
| |- 5 # [6, [[7, []]]]
|
1963
|
+
| |- 6 # ]],
|
1964
|
+
| |- 7 # [8, []]
|
1965
|
+
|- 8 # ]]
|
1966
|
+
|
1967
|
+
# ids_matrix # {
|
1968
|
+
0 | 1 | 2 | 3 | 4 # 0 => [0, 0],
|
1969
|
+
- | - | - | - | - # 1 => [1, 1],
|
1970
|
+
0 | | | | # 2 => [1, 2],
|
1971
|
+
1 | 1 | 2 | | # 3 => [2, 1],
|
1972
|
+
2 | 3 | | | # 4 => [3, 1],
|
1973
|
+
3 | 4 | 5 | 6 | 7 # 5 => [3, 2],
|
1974
|
+
4 | 8 | | | # 6 => [3, 3],
|
1975
|
+
# 7 => [3, 4],
|
1976
|
+
# 8 => [4, 1]
|
1977
|
+
# }
|
1978
|
+
```
|
1979
|
+
|
1980
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1981
|
+
|
1982
|
+
### Configuration
|
1983
|
+
|
1984
|
+
#### Turning on/off
|
1939
1985
|
|
1940
1986
|
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.
|
1941
1987
|
|
@@ -1949,15 +1995,179 @@ result = SumDivisionsByTwo.call(20, 10)
|
|
1949
1995
|
|
1950
1996
|
result.transitions
|
1951
1997
|
|
1952
|
-
{:version=>1, :records=>[], :metadata=>{:duration=>0, :
|
1998
|
+
{:version=>1, :records=>[], :metadata=>{:duration=>0, :ids_tree=>[]}}
|
1953
1999
|
```
|
1954
2000
|
|
1955
2001
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1956
2002
|
|
1957
|
-
|
2003
|
+
#### Setting a `trace_id` fetcher
|
2004
|
+
|
2005
|
+
You can define a lambda (arity 0) to fetch the trace_id. This lambda will be called before the first transition and will be used to set the `:trace_id` in the `:metadata` property.
|
2006
|
+
|
2007
|
+
Use to correlate different or the same operation (executed multiple times).
|
2008
|
+
|
2009
|
+
```ruby
|
2010
|
+
BCDD::Result.config.transitions.trace_id = -> { Thread.current[:bcdd_result_transitions_trace_id] }
|
2011
|
+
```
|
2012
|
+
|
2013
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2014
|
+
|
2015
|
+
#### Setting a `listener`
|
2016
|
+
|
2017
|
+
You can define a listener to be called during the result transitions tracking (check out [this example](examples/single_listener/lib/single_transitions_listener.rb)). It must be a class that includes `BCDD::Result::Transitions::Listener`.
|
2018
|
+
|
2019
|
+
Use it to build your additional logic on top of the transitions tracking. Examples:
|
2020
|
+
- Log the transitions.
|
2021
|
+
- Perform a trace of the transitions.
|
2022
|
+
- Instrument the transitions (measure/report).
|
2023
|
+
- Build a visualization of the transitions (Diagrams, using the `records` + `:ids_tree` and `:ids_matrix` properties).
|
2024
|
+
|
2025
|
+
After implementing your listener, you can set it to the `BCDD::Result.config.transitions.listener=`:
|
2026
|
+
|
2027
|
+
```ruby
|
2028
|
+
BCDD::Result.config.transitions.listener = MyTransitionsListener
|
2029
|
+
```
|
2030
|
+
|
2031
|
+
See the example below to understand how to implement one:
|
2032
|
+
|
2033
|
+
```ruby
|
2034
|
+
class MyTransitionsListener
|
2035
|
+
include BCDD::Result::Transitions::Listener
|
2036
|
+
|
2037
|
+
# A listener will be initialized before the first transition, and it is discarded after the last one.
|
2038
|
+
def initialize
|
2039
|
+
end
|
2040
|
+
|
2041
|
+
# This method will be called before each transition block.
|
2042
|
+
# The parent transition block will be called first in the case of nested transition blocks.
|
2043
|
+
#
|
2044
|
+
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2045
|
+
def on_start(scope:)
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
# This method will wrap all the transitions in the same block.
|
2049
|
+
# It can be used to perform an instrumentation (measure/report) of the transitions.
|
2050
|
+
#
|
2051
|
+
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2052
|
+
def around_transitions(scope:)
|
2053
|
+
yield
|
2054
|
+
end
|
2055
|
+
|
2056
|
+
# This method will wrap each and_then call.
|
2057
|
+
# It can be used to perform an instrumentation (measure/report) of the and_then calls.
|
2058
|
+
#
|
2059
|
+
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2060
|
+
# @param and_then:
|
2061
|
+
# {:type=>:block, :arg=>:some_injected_value}
|
2062
|
+
# {:type=>:method, :arg=>:some_injected_value, :method_name=>:some_method_name}
|
2063
|
+
def around_and_then(scope:, and_then:)
|
2064
|
+
yield
|
2065
|
+
end
|
2066
|
+
|
2067
|
+
# This method will be called after each result recording/tracking.
|
2068
|
+
#
|
2069
|
+
# @param record:
|
2070
|
+
# {
|
2071
|
+
# :root => {:id=>0, :name=>"RootOperation", :desc=>nil},
|
2072
|
+
# :parent => {:id=>0, :name=>"RootOperation", :desc=>nil},
|
2073
|
+
# :current => {:id=>1, :name=>"SomeOperation", :desc=>nil},
|
2074
|
+
# :result => {:kind=>:success, :type=>:_continue_, :value=>{some: :thing}, :source=><MyProcess:0x0000000102fd6378>},
|
2075
|
+
# :and_then => {:type=>:method, :arg=>nil, :method_name=>:some_method},
|
2076
|
+
# :time => 2024-01-26 02:53:11.310431 UTC
|
2077
|
+
# }
|
2078
|
+
def on_record(record:)
|
2079
|
+
end
|
2080
|
+
|
2081
|
+
# This method will be called at the end of the transitions tracking.
|
2082
|
+
#
|
2083
|
+
# @param transitions:
|
2084
|
+
# {
|
2085
|
+
# :version => 1,
|
2086
|
+
# :metadata => {
|
2087
|
+
# :duration => 0,
|
2088
|
+
# :trace_id => nil,
|
2089
|
+
# :ids_tree => [0, [[1, []], [2, []]]],
|
2090
|
+
# :ids_matrix => {0 => [0, 0], 1 => [1, 1], 2 => [2, 1]}
|
2091
|
+
# },
|
2092
|
+
# :records => [
|
2093
|
+
# # ...
|
2094
|
+
# ]
|
2095
|
+
# }
|
2096
|
+
def on_finish(transitions:)
|
2097
|
+
end
|
2098
|
+
|
2099
|
+
# This method will be called when an exception is raised during the transitions tracking.
|
2100
|
+
#
|
2101
|
+
# @param exception: Exception
|
2102
|
+
# @param transitions: Hash
|
2103
|
+
def before_interruption(exception:, transitions:)
|
2104
|
+
end
|
2105
|
+
end
|
2106
|
+
```
|
2107
|
+
|
2108
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2109
|
+
|
2110
|
+
#### Setting multiple `listeners`
|
2111
|
+
|
2112
|
+
You can use `BCDD::Result::Transitions::Listeners[]` to creates a listener of listeners (check out [this example](examples/multiple_listeners/Rakefile)), which will be called in the order they were added.
|
2113
|
+
|
2114
|
+
**Attention:** It only allows one listener to handle `around_and_then` and another `around_transitions` events.
|
2115
|
+
|
2116
|
+
> The example below defines different listeners to handle `around_and_then` and `around_transitions,` but it is also possible to define a listener to handle both.
|
2117
|
+
|
2118
|
+
```ruby
|
2119
|
+
class AroundAndThenListener
|
2120
|
+
include BCDD::Result::Transitions::Listener
|
2121
|
+
|
2122
|
+
# It must be a static/singleton method.
|
2123
|
+
def self.around_and_then?
|
2124
|
+
true
|
2125
|
+
end
|
2126
|
+
|
2127
|
+
def around_and_then(scope:, and_then:)
|
2128
|
+
#...
|
2129
|
+
end
|
2130
|
+
end
|
2131
|
+
|
2132
|
+
class AroundTransitionsListener
|
2133
|
+
include BCDD::Result::Transitions::Listener
|
2134
|
+
|
2135
|
+
# It must be a static/singleton method.
|
2136
|
+
def self.around_transitions?
|
2137
|
+
true
|
2138
|
+
end
|
2139
|
+
|
2140
|
+
def around_transitions(scope:)
|
2141
|
+
#...
|
2142
|
+
end
|
2143
|
+
end
|
2144
|
+
|
2145
|
+
class MyTransitionsListener
|
2146
|
+
include BCDD::Result::Transitions::Listener
|
2147
|
+
end
|
2148
|
+
```
|
2149
|
+
|
2150
|
+
How to use it:
|
2151
|
+
|
2152
|
+
```ruby
|
2153
|
+
# The listeners will be called in the order they were added.
|
2154
|
+
BCDD::Result.config.transitions.listener = BCDD::Result::Transitions::Listeners[
|
2155
|
+
MyTransitionsListener,
|
2156
|
+
AroundAndThenListener,
|
2157
|
+
AroundTransitionsListener
|
2158
|
+
]
|
2159
|
+
```
|
2160
|
+
|
2161
|
+
> Check out [this example](examples/multiple_listeners) to see a listener to print the transitions and another to store them in the database.
|
2162
|
+
|
2163
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2164
|
+
|
2165
|
+
## `BCDD::Result.configuration`
|
1958
2166
|
|
1959
2167
|
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.
|
1960
2168
|
|
2169
|
+
> Note: You can use `BCDD::Result.configuration(freeze: false) {}` to avoid the freezing. This can be useful in tests. Please be sure to use it with caution.
|
2170
|
+
|
1961
2171
|
```ruby
|
1962
2172
|
BCDD::Result.configuration do |config|
|
1963
2173
|
config.addon.enable!(:given, :continue)
|
@@ -1974,13 +2184,13 @@ Use `disable!` to disable a feature and `enable!` to enable it.
|
|
1974
2184
|
|
1975
2185
|
Let's see what each configuration in the example above does:
|
1976
2186
|
|
1977
|
-
|
2187
|
+
### `config.addon.enable!(:given, :continue)` <!-- omit in toc -->
|
1978
2188
|
|
1979
2189
|
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).
|
1980
2190
|
|
1981
2191
|
It is also enabling the `Given()` which is already enabled by default. Link to documentation: [(1)](#add-ons) [(2)](#mixin-add-ons).
|
1982
2192
|
|
1983
|
-
|
2193
|
+
### `config.constant_alias.enable!('Result', 'BCDD::Context')` <!-- omit in toc -->
|
1984
2194
|
|
1985
2195
|
This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::Result::Context`.
|
1986
2196
|
|
@@ -1988,23 +2198,23 @@ Link to documentations:
|
|
1988
2198
|
- [Result alias](#bcddresult-versus-result)
|
1989
2199
|
- [Context aliases](#constant-aliases)
|
1990
2200
|
|
1991
|
-
|
2201
|
+
### `config.pattern_matching.disable!(:nil_as_valid_value_checking)` <!-- omit in toc -->
|
1992
2202
|
|
1993
2203
|
This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::Result::Context`. Link to [documentation](#pattern-matching-support).
|
1994
2204
|
|
1995
|
-
|
1996
|
-
|
1997
|
-
#### `config.feature.disable!(:expectations)`
|
2205
|
+
### `config.feature.disable!(:expectations)` <!-- omit in toc -->
|
1998
2206
|
|
1999
2207
|
This configuration turns off the expectations for `BCDD::Result` and `BCDD::Result::Context`. The expectations are helpful in development and test environments, but they can be disabled in production environments for performance gain.
|
2000
2208
|
|
2001
2209
|
PS: I'm using `::Rails.env.production?` to check the environment, but you can use any logic you want.
|
2002
2210
|
|
2211
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2212
|
+
|
2003
2213
|
### `BCDD::Result.config`
|
2004
2214
|
|
2005
2215
|
The `BCDD::Result.config` allows you to access the current configuration.
|
2006
2216
|
|
2007
|
-
**BCDD::Result.config.addon**
|
2217
|
+
#### **BCDD::Result.config.addon** <!-- omit in toc -->
|
2008
2218
|
|
2009
2219
|
```ruby
|
2010
2220
|
BCDD::Result.config.addon.enabled?(:continue)
|
@@ -2033,7 +2243,7 @@ BCDD::Result.config.addon.options
|
|
2033
2243
|
# }
|
2034
2244
|
```
|
2035
2245
|
|
2036
|
-
**BCDD::Result.config.constant_alias**
|
2246
|
+
#### **BCDD::Result.config.constant_alias** <!-- omit in toc -->
|
2037
2247
|
|
2038
2248
|
```ruby
|
2039
2249
|
BCDD::Result.config.constant_alias.enabled?('Result')
|
@@ -2048,7 +2258,7 @@ BCDD::Result.config.constant_alias.options
|
|
2048
2258
|
# }
|
2049
2259
|
```
|
2050
2260
|
|
2051
|
-
**BCDD::Result.config.pattern_matching**
|
2261
|
+
#### **BCDD::Result.config.pattern_matching** <!-- omit in toc -->
|
2052
2262
|
|
2053
2263
|
```ruby
|
2054
2264
|
BCDD::Result.config.pattern_matching.enabled?(:nil_as_valid_value_checking)
|
@@ -2065,7 +2275,7 @@ BCDD::Result.config.pattern_matching.options
|
|
2065
2275
|
# }
|
2066
2276
|
```
|
2067
2277
|
|
2068
|
-
**BCDD::Result.config.feature**
|
2278
|
+
#### **BCDD::Result.config.feature** <!-- omit in toc -->
|
2069
2279
|
|
2070
2280
|
```ruby
|
2071
2281
|
BCDD::Result.config.feature.enabled?(:expectations)
|
@@ -2287,7 +2497,7 @@ end
|
|
2287
2497
|
|
2288
2498
|
## About
|
2289
2499
|
|
2290
|
-
[Rodrigo Serradura](https://github.com/serradura) created this project. He is the B/CDD process/method creator and has already made similar gems like the [u-case](https://github.com/serradura/u-case) and [kind](https://github.com/serradura/kind/blob/main/lib/kind/result.rb). This gem
|
2500
|
+
[Rodrigo Serradura](https://github.com/serradura) created this project. He is the B/CDD process/method creator and has already made similar gems like the [u-case](https://github.com/serradura/u-case) and [kind](https://github.com/serradura/kind/blob/main/lib/kind/result.rb). This gem can be used independently, but it also contains essential features that facilitate the adoption of B/CDD in code.
|
2291
2501
|
|
2292
2502
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2293
2503
|
|
data/Steepfile
CHANGED
@@ -10,16 +10,16 @@ target :lib do
|
|
10
10
|
# check 'app/models/**/*.rb' # Glob
|
11
11
|
# ignore 'lib/templates/*.rb'
|
12
12
|
|
13
|
-
library 'singleton'
|
13
|
+
library 'singleton' # Standard libraries
|
14
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
|
18
18
|
# configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
|
19
19
|
# configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
configure_code_diagnostics do |hash| # You can setup everything yourself
|
21
|
+
hash[D::Ruby::NoMethod] = :information
|
22
|
+
end
|
23
23
|
end
|
24
24
|
|
25
25
|
# target :test do
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if RUBY_VERSION <= '3.1'
|
4
|
+
puts 'This example requires Ruby 3.1 or higher.'
|
5
|
+
exit! 1
|
6
|
+
end
|
7
|
+
|
8
|
+
# Usage:
|
9
|
+
#
|
10
|
+
# rake DISABLE_TRANSITIONS=t
|
11
|
+
# rake DISABLE_LISTENER=t
|
12
|
+
#
|
13
|
+
# rake HIDE_GIVEN_AND_CONTINUE=t
|
14
|
+
#
|
15
|
+
# rake BREAK_ACCOUNT_CREATION=t
|
16
|
+
# rake BREAK_USER_CREATION=t
|
17
|
+
# rake BREAK_USER_TOKEN_CREATION=t
|
18
|
+
#
|
19
|
+
# rake BREAK_ACCOUNT_CREATION=t HIDE_GIVEN_AND_CONTINUE=t
|
20
|
+
task default: %i[bcdd_result_transitions]
|
21
|
+
|
22
|
+
desc 'creates an account and an owner user through BCDD::Result'
|
23
|
+
task :bcdd_result_transitions do
|
24
|
+
require_relative 'config'
|
25
|
+
|
26
|
+
BCDD::Result.configuration do |config|
|
27
|
+
config.feature.disable!(:transitions) if ENV['DISABLE_TRANSITIONS']
|
28
|
+
|
29
|
+
unless ENV['DISABLE_LISTENER']
|
30
|
+
config.transitions.listener = BCDD::Result::Transitions::Listeners[
|
31
|
+
TransitionsListener::Stdout,
|
32
|
+
BCDD::Result::TransitionsRecord::Listener
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
result = nil
|
38
|
+
|
39
|
+
bench = Benchmark.measure do
|
40
|
+
result = Account::OwnerCreation.new.call(
|
41
|
+
owner: {
|
42
|
+
name: "\tJohn Doe \n",
|
43
|
+
email: ' JOHN.doe@email.com',
|
44
|
+
password: '123123123',
|
45
|
+
password_confirmation: '123123123'
|
46
|
+
}
|
47
|
+
)
|
48
|
+
rescue RuntimeBreaker::Interruption => e
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
puts "\nBCDD::Result::TransitionsRecord.count: #{BCDD::Result::TransitionsRecord.count}"
|
53
|
+
|
54
|
+
puts "\nBenchmark: #{bench}"
|
55
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Account::Member < ActiveRecord::Base
|
4
|
+
self.table_name = 'account_members'
|
5
|
+
|
6
|
+
enum role: { owner: 0, admin: 1, contributor: 2 }
|
7
|
+
|
8
|
+
belongs_to :user, inverse_of: :memberships
|
9
|
+
belongs_to :account, inverse_of: :memberships
|
10
|
+
end
|