bcdd-result 0.13.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +46 -17
- data/README.md +381 -220
- data/Rakefile +1 -1
- data/Steepfile +1 -1
- data/examples/multiple_listeners/Rakefile +8 -8
- data/examples/multiple_listeners/app/models/account/owner_creation.rb +1 -1
- data/examples/multiple_listeners/app/models/user/creation.rb +1 -1
- data/examples/multiple_listeners/app/models/user/token/creation.rb +1 -1
- data/examples/multiple_listeners/config/initializers/bcdd.rb +0 -2
- data/examples/multiple_listeners/config.rb +3 -3
- data/examples/multiple_listeners/db/setup.rb +2 -3
- data/examples/multiple_listeners/lib/bcdd/result/event_logs_record.rb +27 -0
- data/examples/multiple_listeners/lib/event_logs_listener/stdout.rb +60 -0
- data/examples/multiple_listeners/lib/runtime_breaker.rb +1 -1
- data/examples/service_objects/Rakefile +36 -0
- data/examples/service_objects/app/models/account/member.rb +10 -0
- data/examples/service_objects/app/models/account.rb +11 -0
- data/examples/service_objects/app/models/user/token.rb +7 -0
- data/examples/service_objects/app/models/user.rb +15 -0
- data/examples/service_objects/app/services/account/owner_creation.rb +47 -0
- data/examples/service_objects/app/services/application_service.rb +79 -0
- data/examples/service_objects/app/services/user/creation.rb +56 -0
- data/examples/service_objects/app/services/user/token/creation.rb +37 -0
- data/examples/service_objects/config/boot.rb +17 -0
- data/examples/service_objects/config/initializers/bcdd.rb +9 -0
- data/examples/service_objects/config.rb +20 -0
- data/examples/service_objects/db/setup.rb +49 -0
- data/examples/single_listener/Rakefile +5 -5
- data/examples/single_listener/app/models/account/owner_creation.rb +1 -1
- data/examples/single_listener/app/models/user/creation.rb +1 -1
- data/examples/single_listener/app/models/user/token/creation.rb +1 -1
- data/examples/single_listener/config/initializers/bcdd.rb +0 -2
- data/examples/single_listener/config.rb +1 -1
- data/examples/single_listener/lib/{single_transitions_listener.rb → single_event_logs_listener.rb} +32 -23
- data/lib/bcdd/{result/context → context}/callable_and_then.rb +5 -4
- data/lib/bcdd/{result/context → context}/expectations/mixin.rb +1 -1
- data/lib/bcdd/{result/context → context}/expectations.rb +2 -2
- data/lib/bcdd/context/failure.rb +9 -0
- data/lib/bcdd/{result/context → context}/mixin.rb +2 -2
- data/lib/bcdd/{result/context → context}/success.rb +6 -6
- data/lib/bcdd/context.rb +91 -0
- data/lib/bcdd/failure.rb +23 -0
- data/lib/bcdd/result/_self.rb +198 -0
- data/lib/bcdd/result/callable_and_then/caller.rb +1 -1
- data/lib/bcdd/result/config/switchers/addons.rb +2 -2
- data/lib/bcdd/result/config/switchers/constant_aliases.rb +1 -3
- data/lib/bcdd/result/config/switchers/features.rb +5 -5
- data/lib/bcdd/result/config/switchers/pattern_matching.rb +1 -1
- data/lib/bcdd/result/config.rb +7 -5
- data/lib/bcdd/result/contract/type_checker.rb +4 -0
- data/lib/bcdd/result/{transitions → event_logs}/config.rb +5 -3
- data/lib/bcdd/result/{transitions → event_logs}/listener.rb +5 -5
- data/lib/bcdd/result/{transitions → event_logs}/listeners.rb +17 -17
- data/lib/bcdd/result/{transitions → event_logs}/tracking/disabled.rb +1 -1
- data/lib/bcdd/result/{transitions → event_logs}/tracking/enabled.rb +15 -13
- data/lib/bcdd/result/{transitions → event_logs}/tracking.rb +4 -3
- data/lib/bcdd/result/{transitions → event_logs}/tree.rb +27 -11
- data/lib/bcdd/result/event_logs.rb +27 -0
- data/lib/bcdd/result/failure.rb +1 -3
- data/lib/bcdd/result/success.rb +1 -3
- data/lib/bcdd/result/version.rb +1 -1
- data/lib/bcdd/result.rb +23 -191
- data/lib/bcdd/success.rb +23 -0
- data/sig/bcdd/context.rbs +175 -0
- data/sig/bcdd/failure.rbs +13 -0
- data/sig/bcdd/result/config.rbs +1 -3
- data/sig/bcdd/result/context.rbs +2 -174
- data/sig/bcdd/result/contract.rbs +1 -0
- data/sig/bcdd/result/{transitions.rbs → event_logs.rbs} +19 -19
- data/sig/bcdd/result.rbs +13 -31
- data/sig/bcdd/success.rbs +13 -0
- metadata +41 -24
- data/examples/multiple_listeners/lib/bcdd/result/transitions_record.rb +0 -28
- data/examples/multiple_listeners/lib/transitions_listener/stdout.rb +0 -54
- data/lib/bcdd/result/context/failure.rb +0 -9
- data/lib/bcdd/result/context.rb +0 -93
- data/lib/bcdd/result/failure/methods.rb +0 -21
- data/lib/bcdd/result/success/methods.rb +0 -21
- data/lib/bcdd/result/transitions.rb +0 -27
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
<h1 align="center" id="-bcddresult">🔀 BCDD::Result</h1>
|
3
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
|
-
<img src="https://img.shields.io/badge/
|
5
|
+
<img src="https://img.shields.io/badge/Ruby%20%3E%3D%202.7%2C%20%3C%3D%20Head-ruby.svg?colorA=444&colorB=333" 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
7
|
<a href="https://codeclimate.com/github/B-CDD/result/maintainability"><img src="https://api.codeclimate.com/v1/badges/aa8360f8f012d7dedd62/maintainability" /></a>
|
8
8
|
<a href="https://codeclimate.com/github/B-CDD/result/test_coverage"><img src="https://api.codeclimate.com/v1/badges/aa8360f8f012d7dedd62/test_coverage" /></a>
|
@@ -19,12 +19,13 @@ Furthermore, this abstraction exposes several features that will be useful to ma
|
|
19
19
|
|
20
20
|
Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) pattern (superpower) in your code.
|
21
21
|
|
22
|
-
- [Ruby
|
22
|
+
- [Supported Ruby](#supported-ruby)
|
23
23
|
- [Installation](#installation)
|
24
24
|
- [Usage](#usage)
|
25
25
|
- [`BCDD::Result` *versus* `Result`](#bcddresult-versus-result)
|
26
26
|
- [Reference](#reference)
|
27
|
-
- [
|
27
|
+
- [Basic methods](#basic-methods)
|
28
|
+
- [Checking types with `result.is?` or `method missing`](#checking-types-with-resultis-or-method-missing)
|
28
29
|
- [Checking types with `result.success?` or `result.failure?`](#checking-types-with-resultsuccess-or-resultfailure)
|
29
30
|
- [Result Hooks](#result-hooks)
|
30
31
|
- [`result.on`](#resulton)
|
@@ -37,9 +38,6 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
|
|
37
38
|
- [`result.value_or`](#resultvalue_or)
|
38
39
|
- [Result Data](#result-data)
|
39
40
|
- [`result.data`](#resultdata)
|
40
|
-
- [Pattern Matching](#pattern-matching)
|
41
|
-
- [`Array`/`Find` patterns](#arrayfind-patterns)
|
42
|
-
- [`Hash` patterns](#hash-patterns)
|
43
41
|
- [Railway Oriented Programming](#railway-oriented-programming)
|
44
42
|
- [`result.and_then`](#resultand_then)
|
45
43
|
- [`BCDD::Result.mixin`](#bcddresultmixin)
|
@@ -63,17 +61,25 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
|
|
63
61
|
- [Failure()](#failure)
|
64
62
|
- [Pattern Matching Support](#pattern-matching-support)
|
65
63
|
- [`BCDD::Result::Expectations.mixin` add-ons](#bcddresultexpectationsmixin-add-ons)
|
66
|
-
- [`BCDD::
|
64
|
+
- [`BCDD::Context`](#bcddcontext)
|
67
65
|
- [Defining successes and failures](#defining-successes-and-failures)
|
68
66
|
- [Constant aliases](#constant-aliases)
|
69
|
-
- [`BCDD::
|
67
|
+
- [`BCDD::Context.mixin`](#bcddcontextmixin)
|
70
68
|
- [Class example (Instance Methods)](#class-example-instance-methods-1)
|
71
69
|
- [`and_expose`](#and_expose)
|
72
70
|
- [Module example (Singleton Methods)](#module-example-singleton-methods-1)
|
73
|
-
- [`BCDD::
|
71
|
+
- [`BCDD::Context::Expectations`](#bcddcontextexpectations)
|
74
72
|
- [Mixin add-ons](#mixin-add-ons)
|
75
|
-
- [
|
76
|
-
- [`
|
73
|
+
- [Pattern Matching](#pattern-matching)
|
74
|
+
- [`BCDD::Result`](#bcddresult)
|
75
|
+
- [`Array`/`Find` patterns](#arrayfind-patterns)
|
76
|
+
- [`Hash` patterns](#hash-patterns)
|
77
|
+
- [`BCDD::Context`](#bcddcontext-1)
|
78
|
+
- [`Array`/`Find` patterns](#arrayfind-patterns-1)
|
79
|
+
- [`Hash` patterns](#hash-patterns-1)
|
80
|
+
- [How to pattern match without the concept of success and failure](#how-to-pattern-match-without-the-concept-of-success-and-failure)
|
81
|
+
- [`BCDD::Result.event_logs`](#bcddresultevent_logs)
|
82
|
+
- [`metadata: {ids:}`](#metadata-ids)
|
77
83
|
- [Configuration](#configuration)
|
78
84
|
- [Turning on/off](#turning-onoff)
|
79
85
|
- [Setting a `trace_id` fetcher](#setting-a-trace_id-fetcher)
|
@@ -93,9 +99,13 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
|
|
93
99
|
- [License](#license)
|
94
100
|
- [Code of Conduct](#code-of-conduct)
|
95
101
|
|
96
|
-
## Ruby
|
102
|
+
## Supported Ruby
|
103
|
+
|
104
|
+
This library is tested against:
|
97
105
|
|
98
|
-
|
106
|
+
Version | 2.7 | 3.0 | 3.1 | 3.2 | 3.3 | Head
|
107
|
+
---- | --- | --- | --- | --- | --- | ---
|
108
|
+
100% Coverage | ✅ | ✅ | ✅ | ✅ | ✅ | ✅
|
99
109
|
|
100
110
|
## Installation
|
101
111
|
|
@@ -167,7 +177,7 @@ There are other aliases and configurations available. Check the [BCDD::Result.co
|
|
167
177
|
|
168
178
|
## Reference
|
169
179
|
|
170
|
-
###
|
180
|
+
### Basic methods
|
171
181
|
|
172
182
|
Both `BCDD::Result::Success` and `BCDD::Result::Failure` are composed of the same methods. Look at the basic ones:
|
173
183
|
|
@@ -179,20 +189,22 @@ Both `BCDD::Result::Success` and `BCDD::Result::Failure` are composed of the sam
|
|
179
189
|
################
|
180
190
|
result = BCDD::Result::Success(:ok, my: 'value')
|
181
191
|
|
182
|
-
result.success?
|
183
|
-
result.failure?
|
184
|
-
result.type
|
185
|
-
result.
|
192
|
+
result.success? # true
|
193
|
+
result.failure? # false
|
194
|
+
result.type?(:ok) # true
|
195
|
+
result.type # :ok
|
196
|
+
result.value # {:my => "value"}
|
186
197
|
|
187
198
|
###################
|
188
199
|
# Without a value #
|
189
200
|
###################
|
190
201
|
result = BCDD::Result::Success(:yes)
|
191
202
|
|
192
|
-
result.success?
|
193
|
-
result.failure?
|
194
|
-
result.type
|
195
|
-
result.
|
203
|
+
result.success? # true
|
204
|
+
result.failure? # false
|
205
|
+
result.type?(:yes) # true
|
206
|
+
result.type # :yes
|
207
|
+
result.value # nil
|
196
208
|
```
|
197
209
|
|
198
210
|
**BCDD::Result::Failure**
|
@@ -203,26 +215,46 @@ result.value # nil
|
|
203
215
|
################
|
204
216
|
result = BCDD::Result::Failure(:err, 'my_value')
|
205
217
|
|
206
|
-
result.success?
|
207
|
-
result.failure?
|
208
|
-
result.type
|
209
|
-
result.
|
218
|
+
result.success? # false
|
219
|
+
result.failure? # true
|
220
|
+
result.type?(:err) # true
|
221
|
+
result.type # :err
|
222
|
+
result.value # "my_value"
|
210
223
|
|
211
224
|
###################
|
212
225
|
# Without a value #
|
213
226
|
###################
|
214
227
|
result = BCDD::Result::Failure(:no)
|
215
228
|
|
216
|
-
result.success?
|
217
|
-
result.failure?
|
218
|
-
result.type
|
219
|
-
result.
|
229
|
+
result.success? # false
|
230
|
+
result.failure? # true
|
231
|
+
result.type?(:no) # true
|
232
|
+
result.type # :no
|
233
|
+
result.value # nil
|
220
234
|
```
|
221
235
|
|
222
236
|
In both cases, the `type` must be a symbol, and the `value` can be any kind of object.
|
223
237
|
|
224
238
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
225
239
|
|
240
|
+
#### Checking types with `result.is?` or `method missing`
|
241
|
+
|
242
|
+
Beyond the `type?` method, you can also use the `is?` method to check the result type. If you want to check the type directly, you can write the type using a method that ends with a question mark.
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
result = BCDD::Result::Success(:ok)
|
246
|
+
|
247
|
+
result.is?(:ok) # true
|
248
|
+
result.ok? # true
|
249
|
+
|
250
|
+
result = BCDD::Result::Failure(:err)
|
251
|
+
|
252
|
+
result.is?(:err) # true
|
253
|
+
result.err? # true
|
254
|
+
```
|
255
|
+
|
256
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
257
|
+
|
226
258
|
#### Checking types with `result.success?` or `result.failure?`
|
227
259
|
|
228
260
|
`BCDD::Result#success?` and `BCDD::Result#failure?` are methods that allow you to check if the result is a success or a failure.
|
@@ -533,67 +565,6 @@ print_to_hash(**success_data) # [:success, :ok, 1]
|
|
533
565
|
|
534
566
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
535
567
|
|
536
|
-
### Pattern Matching
|
537
|
-
|
538
|
-
The `BCDD::Result` also provides support to pattern matching.
|
539
|
-
|
540
|
-
In the further examples, I will use the `Divide` lambda to exemplify its usage.
|
541
|
-
|
542
|
-
```ruby
|
543
|
-
Divide = lambda do |arg1, arg2|
|
544
|
-
arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric')
|
545
|
-
arg2.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg2 must be numeric')
|
546
|
-
|
547
|
-
return BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?
|
548
|
-
|
549
|
-
BCDD::Result::Success(:division_completed, arg1 / arg2)
|
550
|
-
end
|
551
|
-
```
|
552
|
-
|
553
|
-
#### `Array`/`Find` patterns
|
554
|
-
|
555
|
-
```ruby
|
556
|
-
case Divide.call(4, 2)
|
557
|
-
in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
|
558
|
-
in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
|
559
|
-
in BCDD::Result::Success[:division_completed, value] then puts value
|
560
|
-
end
|
561
|
-
|
562
|
-
# The code above will print: 2
|
563
|
-
|
564
|
-
case Divide.call(4, 0)
|
565
|
-
in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
|
566
|
-
in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
|
567
|
-
in BCDD::Result::Success[:division_completed, value] then puts value
|
568
|
-
end
|
569
|
-
|
570
|
-
# The code above will print: arg2 must not be zero
|
571
|
-
```
|
572
|
-
|
573
|
-
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
574
|
-
|
575
|
-
#### `Hash` patterns
|
576
|
-
|
577
|
-
```ruby
|
578
|
-
case Divide.call(10, 2)
|
579
|
-
in { failure: { invalid_arg: msg } } then puts msg
|
580
|
-
in { failure: { division_by_zero: msg } } then puts msg
|
581
|
-
in { success: { division_completed: value } } then puts value
|
582
|
-
end
|
583
|
-
|
584
|
-
# The code above will print: 5
|
585
|
-
|
586
|
-
case Divide.call('10', 2)
|
587
|
-
in { failure: { invalid_arg: msg } } then puts msg
|
588
|
-
in { failure: { division_by_zero: msg } } then puts msg
|
589
|
-
in { success: { division_completed: value } } then puts value
|
590
|
-
end
|
591
|
-
|
592
|
-
# The code above will print: arg1 must be numeric
|
593
|
-
```
|
594
|
-
|
595
|
-
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
596
|
-
|
597
568
|
### Railway Oriented Programming
|
598
569
|
|
599
570
|
["Railway Oriented Programming (ROP)"](https://fsharpforfunandprofit.com/rop/) is a programming technique that involves linking blocks together to form a sequence of operations, also known as a pipeline.
|
@@ -1429,45 +1400,45 @@ result.success?(:ok)
|
|
1429
1400
|
|
1430
1401
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1431
1402
|
|
1432
|
-
### `BCDD::
|
1403
|
+
### `BCDD::Context`
|
1433
1404
|
|
1434
|
-
The `BCDD::
|
1405
|
+
The `BCDD::Context` is a `BCDD::Result`, meaning it has all the features of the `BCDD::Result`. The main difference is that it only accepts keyword arguments as a value, which applies to the `and_then`: The called methods must receive keyword arguments, and the dependency injection will be performed through keyword arguments.
|
1435
1406
|
|
1436
|
-
As the input/output are hashes, the results of each `and_then` call will automatically accumulate. This is useful in operations chaining, as the result of the previous operations will be automatically available for the next one. Because of this behavior, the `BCDD::
|
1407
|
+
As the input/output are hashes, the results of each `and_then` call will automatically accumulate. This is useful in operations chaining, as the result of the previous operations will be automatically available for the next one. Because of this behavior, the `BCDD::Context` has the `#and_expose` method to expose only the desired keys from the accumulated result.
|
1437
1408
|
|
1438
1409
|
#### Defining successes and failures
|
1439
1410
|
|
1440
|
-
As the `BCDD::Result`, you can declare success and failures directly from `BCDD::
|
1411
|
+
As the `BCDD::Result`, you can declare success and failures directly from `BCDD::Context`.
|
1441
1412
|
|
1442
1413
|
```ruby
|
1443
|
-
BCDD::
|
1444
|
-
#<BCDD::
|
1414
|
+
BCDD::Context::Success(:ok, a: 1, b: 2)
|
1415
|
+
#<BCDD::Context::Success type=:ok value={:a=>1, :b=>2}>
|
1445
1416
|
|
1446
|
-
BCDD::
|
1447
|
-
#<BCDD::
|
1417
|
+
BCDD::Context::Failure(:err, message: 'something went wrong')
|
1418
|
+
#<BCDD::Context::Failure type=:err value={:message=>"something went wrong"}>
|
1448
1419
|
```
|
1449
1420
|
|
1450
|
-
But different from `BCDD::Result` that accepts any value, the `BCDD::
|
1421
|
+
But different from `BCDD::Result` that accepts any value, the `BCDD::Context` only takes keyword arguments.
|
1451
1422
|
|
1452
1423
|
```ruby
|
1453
|
-
BCDD::
|
1424
|
+
BCDD::Context::Success(:ok, [1, 2])
|
1454
1425
|
# wrong number of arguments (given 2, expected 1) (ArgumentError)
|
1455
1426
|
|
1456
|
-
BCDD::
|
1427
|
+
BCDD::Context::Failure(:err, { message: 'something went wrong' })
|
1457
1428
|
# wrong number of arguments (given 2, expected 1) (ArgumentError)
|
1458
1429
|
|
1459
1430
|
#
|
1460
1431
|
# Use ** to convert a hash to keyword arguments
|
1461
1432
|
#
|
1462
|
-
BCDD::
|
1463
|
-
#<BCDD::
|
1433
|
+
BCDD::Context::Success(:ok, **{ message: 'hashes can be converted to keyword arguments' })
|
1434
|
+
#<BCDD::Context::Success type=:ok value={:message=>"hashes can be converted to keyword arguments"}>
|
1464
1435
|
```
|
1465
1436
|
|
1466
1437
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1467
1438
|
|
1468
1439
|
#### Constant aliases
|
1469
1440
|
|
1470
|
-
You can configure `Context` or `BCDD::Context` as an alias for `BCDD::
|
1441
|
+
You can configure `Context` or `BCDD::Context` as an alias for `BCDD::Context`. This is helpful to define a standard way to avoid the full constant name/path in your code.
|
1471
1442
|
|
1472
1443
|
```ruby
|
1473
1444
|
BCDD::Result.configuration do |config|
|
@@ -1481,9 +1452,9 @@ end
|
|
1481
1452
|
|
1482
1453
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1483
1454
|
|
1484
|
-
#### `BCDD::
|
1455
|
+
#### `BCDD::Context.mixin`
|
1485
1456
|
|
1486
|
-
As in the `BCDD::Result`, you can use the `BCDD::
|
1457
|
+
As in the `BCDD::Result`, you can use the `BCDD::Context.mixin` to add the `Success()` and `Failure()` methods to your classes/modules.
|
1487
1458
|
|
1488
1459
|
Let's see this feature and the data accumulation in action:
|
1489
1460
|
|
@@ -1493,7 +1464,7 @@ Let's see this feature and the data accumulation in action:
|
|
1493
1464
|
require 'logger'
|
1494
1465
|
|
1495
1466
|
class Divide
|
1496
|
-
include BCDD::
|
1467
|
+
include BCDD::Context.mixin
|
1497
1468
|
|
1498
1469
|
def call(arg1, arg2, logger: ::Logger.new(STDOUT))
|
1499
1470
|
validate_numbers(arg1, arg2)
|
@@ -1530,29 +1501,29 @@ end
|
|
1530
1501
|
|
1531
1502
|
Divide.new.call(10, 5)
|
1532
1503
|
# I, [2023-10-27T01:51:46.905004 #76915] INFO -- : The division result is 2
|
1533
|
-
#<BCDD::
|
1504
|
+
#<BCDD::Context::Success type=:ok value={:number=>2}>
|
1534
1505
|
|
1535
1506
|
Divide.new.call('10', 5)
|
1536
|
-
#<BCDD::
|
1507
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg1 must be numeric"}>
|
1537
1508
|
|
1538
1509
|
Divide.new.call(10, '5')
|
1539
|
-
#<BCDD::
|
1510
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must be numeric"}>
|
1540
1511
|
|
1541
1512
|
Divide.new.call(10, 0)
|
1542
|
-
#<BCDD::
|
1513
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must not be zero"}>
|
1543
1514
|
```
|
1544
1515
|
|
1545
1516
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1546
1517
|
|
1547
1518
|
##### `and_expose`
|
1548
1519
|
|
1549
|
-
This allows you to expose only the desired keys from the accumulated result. It can be used with any `BCDD::
|
1520
|
+
This allows you to expose only the desired keys from the accumulated result. It can be used with any `BCDD::Context` object.
|
1550
1521
|
|
1551
1522
|
Let's add it to the previous example:
|
1552
1523
|
|
1553
1524
|
```ruby
|
1554
1525
|
class Divide
|
1555
|
-
include BCDD::
|
1526
|
+
include BCDD::Context.mixin
|
1556
1527
|
|
1557
1528
|
def call(arg1, arg2)
|
1558
1529
|
validate_numbers(arg1, arg2)
|
@@ -1582,7 +1553,7 @@ class Divide
|
|
1582
1553
|
end
|
1583
1554
|
|
1584
1555
|
Divide.new.call(10, 5)
|
1585
|
-
#<BCDD::
|
1556
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1586
1557
|
```
|
1587
1558
|
|
1588
1559
|
As you can see, even with `divide` success exposing the division number with all the accumulated data (`**input`), the `#and_expose` could generate a new success with a new type and only with the desired keys.
|
@@ -1591,7 +1562,7 @@ Remove the `#and_expose` call to see the difference. This will be the outcome:
|
|
1591
1562
|
|
1592
1563
|
```ruby
|
1593
1564
|
Divide.new.call(10, 5)
|
1594
|
-
#<BCDD::
|
1565
|
+
#<BCDD::Context::Success type=:ok value={:number=>2, :number1=>10, :number2=>5}>
|
1595
1566
|
```
|
1596
1567
|
|
1597
1568
|
> PS: The `#and_expose` produces a terminal success by default. This means the next step will not be executed even if you call `#and_then` after `#and_expose`. To change this behavior, you can pass `terminal: false` to `#and_expose`.
|
@@ -1600,11 +1571,11 @@ Divide.new.call(10, 5)
|
|
1600
1571
|
|
1601
1572
|
##### Module example (Singleton Methods)
|
1602
1573
|
|
1603
|
-
`BCDD::
|
1574
|
+
`BCDD::Context.mixin` can also produce singleton methods. Below is an example using a module (but it could be a class, too).
|
1604
1575
|
|
1605
1576
|
```ruby
|
1606
1577
|
module Divide
|
1607
|
-
extend self, BCDD::
|
1578
|
+
extend self, BCDD::Context.mixin
|
1608
1579
|
|
1609
1580
|
def call(arg1, arg2)
|
1610
1581
|
validate_numbers(arg1, arg2)
|
@@ -1634,29 +1605,29 @@ module Divide
|
|
1634
1605
|
end
|
1635
1606
|
|
1636
1607
|
Divide.call(10, 5)
|
1637
|
-
#<BCDD::
|
1608
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1638
1609
|
|
1639
1610
|
Divide.call('10', 5)
|
1640
|
-
#<BCDD::
|
1611
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg1 must be numeric"}>
|
1641
1612
|
|
1642
1613
|
Divide.call(10, '5')
|
1643
|
-
#<BCDD::
|
1614
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must be numeric"}>
|
1644
1615
|
|
1645
1616
|
Divide.call(10, 0)
|
1646
|
-
#<BCDD::
|
1617
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must not be zero"}>
|
1647
1618
|
```
|
1648
1619
|
|
1649
1620
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1650
1621
|
|
1651
|
-
#### `BCDD::
|
1622
|
+
#### `BCDD::Context::Expectations`
|
1652
1623
|
|
1653
|
-
The `BCDD::
|
1624
|
+
The `BCDD::Context::Expectations` is a `BCDD::Result::Expectations` with the `BCDD::Context` features.
|
1654
1625
|
|
1655
1626
|
This is an example using the mixin mode, but the standalone mode is also supported.
|
1656
1627
|
|
1657
1628
|
```ruby
|
1658
1629
|
class Divide
|
1659
|
-
include BCDD::
|
1630
|
+
include BCDD::Context::Expectations.mixin(
|
1660
1631
|
config: {
|
1661
1632
|
pattern_matching: { nil_as_valid_value_checking: true }
|
1662
1633
|
},
|
@@ -1680,16 +1651,16 @@ class Divide
|
|
1680
1651
|
end
|
1681
1652
|
|
1682
1653
|
Divide.new.call(10, 5)
|
1683
|
-
#<BCDD::
|
1654
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1684
1655
|
```
|
1685
1656
|
|
1686
|
-
As in the `BCDD::Result::Expectations.mixin`, the `BCDD::
|
1657
|
+
As in the `BCDD::Result::Expectations.mixin`, the `BCDD::Context::Expectations.mixin` will add a Result constant in the target class. It can generate success/failure results, which ensure the mixin expectations.
|
1687
1658
|
|
1688
1659
|
Let's see this using the previous example:
|
1689
1660
|
|
1690
1661
|
```ruby
|
1691
1662
|
Divide::Result::Success(:division_completed, number: 2)
|
1692
|
-
#<BCDD::
|
1663
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1693
1664
|
|
1694
1665
|
Divide::Result::Success(:division_completed, number: '2')
|
1695
1666
|
# value {:number=>"2"} is not allowed for :division_completed type ({:number=>"2"}: Numeric === "2" does not return true) (BCDD::Result::Contract::Error::UnexpectedValue)
|
@@ -1699,7 +1670,7 @@ Divide::Result::Success(:division_completed, number: '2')
|
|
1699
1670
|
|
1700
1671
|
#### Mixin add-ons
|
1701
1672
|
|
1702
|
-
The `BCDD::
|
1673
|
+
The `BCDD::Context.mixin` and `BCDD::Context::Expectations.mixin` also accepts the `config:` argument. And it works the same way as the `BCDD::Result` mixins.
|
1703
1674
|
|
1704
1675
|
**given**
|
1705
1676
|
|
@@ -1707,21 +1678,21 @@ This addon is enabled by default. It will create the `Given(*value)` method. Use
|
|
1707
1678
|
|
1708
1679
|
You can turn it off by passing `given: false` to the `config:` argument or using the `BCDD::Result.configuration`.
|
1709
1680
|
|
1710
|
-
The `Given()` addon for a BCDD::
|
1681
|
+
The `Given()` addon for a BCDD::Context can be called with one or more arguments. The arguments will be converted to a hash (`to_h`) and merged to define the first value of the result chain.
|
1711
1682
|
|
1712
1683
|
**continue**
|
1713
1684
|
|
1714
|
-
The `BCDD::
|
1685
|
+
The `BCDD::Context.mixin(config: { addon: { continue: true } })` or `BCDD::Context::Expectations.mixin(config: { addon: { continue: true } })` creates the `Continue(value)` method and change the `Success()` behavior to terminate the step chain.
|
1715
1686
|
|
1716
1687
|
So, if you want to advance to the next step, you must use `Continue(**value)` instead of `Success(type, **value)`. Otherwise, the step chain will be terminated.
|
1717
1688
|
|
1718
|
-
Let's use a mix of `BCDD::
|
1689
|
+
Let's use a mix of `BCDD::Context` features to see in action with this add-on:
|
1719
1690
|
|
1720
1691
|
```ruby
|
1721
1692
|
module Division
|
1722
1693
|
require 'logger'
|
1723
1694
|
|
1724
|
-
extend self, BCDD::
|
1695
|
+
extend self, BCDD::Context::Expectations.mixin(
|
1725
1696
|
config: {
|
1726
1697
|
addon: { continue: true },
|
1727
1698
|
pattern_matching: { nil_as_valid_value_checking: true }
|
@@ -1771,35 +1742,193 @@ end
|
|
1771
1742
|
|
1772
1743
|
Division.call(14, 2)
|
1773
1744
|
# I, [2023-10-27T02:01:05.812388 #77823] INFO -- : The division result is 7
|
1774
|
-
#<BCDD::
|
1745
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>7}>
|
1775
1746
|
|
1776
1747
|
Division.call(0, 2)
|
1777
|
-
##<BCDD::
|
1748
|
+
##<BCDD::Context::Success type=:division_completed value={:number=>0}>
|
1778
1749
|
|
1779
1750
|
Division.call('14', 2)
|
1780
|
-
#<BCDD::
|
1751
|
+
#<BCDD::Context::Failure type=:invalid_arg value={:message=>"arg1 must be numeric"}>
|
1781
1752
|
|
1782
1753
|
Division.call(14, '2')
|
1783
|
-
#<BCDD::
|
1754
|
+
#<BCDD::Context::Failure type=:invalid_arg value={:message=>"arg2 must be numeric"}>
|
1784
1755
|
|
1785
1756
|
Division.call(14, 0)
|
1786
|
-
#<BCDD::
|
1757
|
+
#<BCDD::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
|
1758
|
+
```
|
1759
|
+
|
1760
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1761
|
+
|
1762
|
+
## Pattern Matching
|
1763
|
+
|
1764
|
+
The `BCDD::Result` and `BCDD::Context` also provides support to pattern matching.
|
1765
|
+
|
1766
|
+
### `BCDD::Result`
|
1767
|
+
|
1768
|
+
In the further examples, I will use the `Divide` lambda to exemplify its usage.
|
1769
|
+
|
1770
|
+
```ruby
|
1771
|
+
Divide = lambda do |arg1, arg2|
|
1772
|
+
arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric')
|
1773
|
+
arg2.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg2 must be numeric')
|
1774
|
+
|
1775
|
+
return BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?
|
1776
|
+
|
1777
|
+
BCDD::Result::Success(:division_completed, arg1 / arg2)
|
1778
|
+
end
|
1779
|
+
```
|
1780
|
+
|
1781
|
+
#### `Array`/`Find` patterns
|
1782
|
+
|
1783
|
+
```ruby
|
1784
|
+
case Divide.call(4, 2)
|
1785
|
+
in BCDD::Failure[:invalid_arg, msg] then puts msg
|
1786
|
+
in BCDD::Failure[:division_by_zero, msg] then puts msg
|
1787
|
+
in BCDD::Success[:division_completed, num] then puts num
|
1788
|
+
end
|
1789
|
+
|
1790
|
+
# The code above will print: 2
|
1791
|
+
|
1792
|
+
case Divide.call(4, 0)
|
1793
|
+
in BCDD::Failure[:invalid_arg, msg] then puts msg
|
1794
|
+
in BCDD::Failure[:division_by_zero, msg] then puts msg
|
1795
|
+
in BCDD::Success[:division_completed, num] then puts num
|
1796
|
+
end
|
1797
|
+
|
1798
|
+
# The code above will print: arg2 must not be zero
|
1799
|
+
```
|
1800
|
+
|
1801
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1802
|
+
|
1803
|
+
#### `Hash` patterns
|
1804
|
+
|
1805
|
+
```ruby
|
1806
|
+
case Divide.call(10, 2)
|
1807
|
+
in BCDD::Failure(type: :invalid_arg, value: msg) then puts msg
|
1808
|
+
in BCDD::Failure(type: :division_by_zero, value: msg) then puts msg
|
1809
|
+
in BCDD::Success(type: :division_completed, value: num) then puts num
|
1810
|
+
end
|
1811
|
+
|
1812
|
+
# The code above will print: 5
|
1813
|
+
|
1814
|
+
case Divide.call('10', 2)
|
1815
|
+
in BCDD::Failure(type: :invalid_arg, value: msg) then puts msg
|
1816
|
+
in BCDD::Failure(type: :division_by_zero, value: msg) then puts msg
|
1817
|
+
in BCDD::Success(type: :division_completed, value: num) then puts num
|
1818
|
+
end
|
1819
|
+
|
1820
|
+
# The code above will print: arg1 must be numeric
|
1821
|
+
```
|
1822
|
+
|
1823
|
+
You can also use `BCDD::Result::Success` and `BCDD::Result::Failure` as patterns.
|
1824
|
+
|
1825
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1826
|
+
|
1827
|
+
|
1828
|
+
### `BCDD::Context`
|
1829
|
+
|
1830
|
+
In the further examples, I will use the `Divide` lambda to exemplify its usage.
|
1831
|
+
|
1832
|
+
```ruby
|
1833
|
+
Divide = lambda do |arg1, arg2|
|
1834
|
+
arg1.is_a?(::Numeric) or return BCDD::Context::Failure(:invalid_arg, err: 'arg1 must be numeric')
|
1835
|
+
arg2.is_a?(::Numeric) or return BCDD::Context::Failure(:invalid_arg, err: 'arg2 must be numeric')
|
1836
|
+
|
1837
|
+
return BCDD::Context::Failure(:division_by_zero, err: 'arg2 must not be zero') if arg2.zero?
|
1838
|
+
|
1839
|
+
BCDD::Context::Success(:division_completed, num: arg1 / arg2)
|
1840
|
+
end
|
1841
|
+
```
|
1842
|
+
|
1843
|
+
#### `Array`/`Find` patterns
|
1844
|
+
|
1845
|
+
```ruby
|
1846
|
+
case Divide.call(4, 2)
|
1847
|
+
in BCDD::Failure[:invalid_arg, {msg:}] then puts msg
|
1848
|
+
in BCDD::Failure[:division_by_zero, {msg:}] then puts msg
|
1849
|
+
in BCDD::Success[:division_completed, {num:}] then puts num
|
1850
|
+
end
|
1851
|
+
|
1852
|
+
# The code above will print: 2
|
1853
|
+
|
1854
|
+
case Divide.call(4, 0)
|
1855
|
+
in BCDD::Failure[:invalid_arg, {msg:}] then puts msg
|
1856
|
+
in BCDD::Failure[:division_by_zero, {msg:}] then puts msg
|
1857
|
+
in BCDD::Success[:division_completed, {num:}] then puts num
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
# The code above will print: arg2 must not be zero
|
1861
|
+
```
|
1862
|
+
|
1863
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1864
|
+
|
1865
|
+
#### `Hash` patterns
|
1866
|
+
|
1867
|
+
If you don't provide the keys :type and :value, the pattern will match the result value.
|
1868
|
+
|
1869
|
+
```ruby
|
1870
|
+
case Divide.call(10, 2)
|
1871
|
+
in BCDD::Failure({msg:}) then puts msg
|
1872
|
+
in BCDD::Success({num:}) then puts num
|
1873
|
+
end
|
1874
|
+
```
|
1875
|
+
|
1876
|
+
```ruby
|
1877
|
+
case Divide.call(10, 2)
|
1878
|
+
in BCDD::Failure(type: :invalid_arg, value: {msg:}) then puts msg
|
1879
|
+
in BCDD::Failure(type: :division_by_zero, value: {msg:}) then puts msg
|
1880
|
+
in BCDD::Success(type: :division_completed, value: {num:}) then puts num
|
1881
|
+
end
|
1882
|
+
|
1883
|
+
# The code above will print: 5
|
1884
|
+
|
1885
|
+
case Divide.call('10', 2)
|
1886
|
+
in BCDD::Failure(type: :invalid_arg, value: {msg:}) then puts {msg:}
|
1887
|
+
in BCDD::Failure(type: :division_by_zero, value: {msg:}) then puts msg
|
1888
|
+
in BCDD::Success(type: :division_completed, value: {num:}) then puts num
|
1889
|
+
end
|
1890
|
+
|
1891
|
+
# The code above will print: arg1 must be numeric
|
1787
1892
|
```
|
1788
1893
|
|
1894
|
+
You can also use `BCDD::Context::Success` and `BCDD::Context::Failure` as patterns.
|
1895
|
+
|
1789
1896
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1790
1897
|
|
1791
|
-
|
1898
|
+
### How to pattern match without the concept of success and failure
|
1899
|
+
|
1900
|
+
You can use the classes `BCDD::Result` and `BCDD::Context` as patterns, and the pattern matching will work without the concept of success and failure.
|
1901
|
+
|
1902
|
+
```ruby
|
1903
|
+
case Divide.call(10, 2)
|
1904
|
+
in BCDD::Context(:invalid_arg, {msg:}) then puts msg
|
1905
|
+
in BCDD::Context(:division_by_zero, {msg:}) then puts msg
|
1906
|
+
in BCDD::Context(:division_completed, {num:}) then puts num
|
1907
|
+
end
|
1908
|
+
|
1909
|
+
case Divide.call(10, 2)
|
1910
|
+
in BCDD::Result(:invalid_arg, msg) then puts msg
|
1911
|
+
in BCDD::Result(:division_by_zero, msg) then puts msg
|
1912
|
+
in BCDD::Result(:division_completed, num) then puts num
|
1913
|
+
end
|
1914
|
+
```
|
1792
1915
|
|
1793
|
-
|
1916
|
+
The `BCDD::Result` will also work with the `BCDD::Context`, but the opposite won't.
|
1794
1917
|
|
1795
|
-
|
1918
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1919
|
+
|
1920
|
+
## `BCDD::Result.event_logs`
|
1921
|
+
|
1922
|
+
Use `BCDD::Result.event_logs(&block)` to track all the results produced in the same or between different operations (it works with `BCDD::Result` and `BCDD::Context`). When there is a nesting of `event_logs` blocks, this mechanism will be able to correlate parent and child blocks and present the duration of all operations in milliseconds.
|
1923
|
+
|
1924
|
+
When you wrap the creation of the result with `BCDD::Result.event_logs`, the final one will expose all the event log records through the `BCDD::Result#event_logs` method.
|
1796
1925
|
|
1797
1926
|
```ruby
|
1798
1927
|
class Division
|
1799
1928
|
include BCDD::Result.mixin(config: { addon: { continue: true } })
|
1800
1929
|
|
1801
1930
|
def call(arg1, arg2)
|
1802
|
-
BCDD::Result.
|
1931
|
+
BCDD::Result.event_logs(name: 'Division', desc: 'divide two numbers') do
|
1803
1932
|
Given([arg1, arg2])
|
1804
1933
|
.and_then(:require_numbers)
|
1805
1934
|
.and_then(:check_for_zeros)
|
@@ -1835,7 +1964,7 @@ module SumDivisionsByTwo
|
|
1835
1964
|
extend self, BCDD::Result.mixin
|
1836
1965
|
|
1837
1966
|
def call(*numbers)
|
1838
|
-
BCDD::Result.
|
1967
|
+
BCDD::Result.event_logs(name: 'SumDivisionsByTwo') do
|
1839
1968
|
divisions = numbers.map { |number| Division.new.call(number, 2) }
|
1840
1969
|
|
1841
1970
|
if divisions.any?(&:failure?)
|
@@ -1854,14 +1983,17 @@ Let's see the result of the `SumDivisionsByTwo` call:
|
|
1854
1983
|
result = SumDivisionsByTwo.call(20, 10)
|
1855
1984
|
# => #<BCDD::Result::Success type=:sum value=15>
|
1856
1985
|
|
1857
|
-
result.
|
1986
|
+
result.event_logs
|
1858
1987
|
{
|
1859
1988
|
:version => 1,
|
1860
1989
|
:metadata => {
|
1861
1990
|
:duration => 0, # milliseconds
|
1862
1991
|
:trace_id => nil, # can be set through configuration
|
1863
|
-
:
|
1864
|
-
|
1992
|
+
:ids => {
|
1993
|
+
:tree => [0, [[1, []], [2, []]]],
|
1994
|
+
:matrix => { 0 => [0, 0], 1 => [1, 1], 2 => [2, 1]},
|
1995
|
+
:level_parent => { 0 => [0, 0], 1 => [1, 0], 2 => [1, 0]}
|
1996
|
+
}
|
1865
1997
|
},
|
1866
1998
|
:records=> [
|
1867
1999
|
{
|
@@ -1942,18 +2074,21 @@ result.transitions
|
|
1942
2074
|
|
1943
2075
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1944
2076
|
|
1945
|
-
### `
|
1946
|
-
|
1947
|
-
The `:ids_matrix`. It is a simplification of the `:ids_tree` property (a graph/tree representation of the transitions ids).
|
2077
|
+
### `metadata: {ids:}`
|
1948
2078
|
|
1949
|
-
The
|
2079
|
+
The `:ids` metadata property is a hash with three properties:
|
2080
|
+
- `:tree`, a graph/tree representation of the id of each `event_logs` block.
|
2081
|
+
- `:level_parent`, a hash with the level (depth) of each block and its parent id.
|
2082
|
+
- `:matrix`, a matrix representation of the event logs ids. It is a simplification of the `:tree` property.
|
1950
2083
|
|
1951
|
-
Use these data structures to build your own visualization
|
2084
|
+
Use these data structures to build your own visualization.
|
1952
2085
|
|
1953
|
-
> Check out [
|
2086
|
+
> Check out [Event Logs Listener example](examples/single_listener/lib/single_event_logs_listener.rb) to see how a listener can be used to build a STDOUT visualization, using these properties.
|
1954
2087
|
|
1955
2088
|
```ruby
|
1956
|
-
#
|
2089
|
+
# tree:
|
2090
|
+
# A graph representation (array of arrays) of the each event logs block id.
|
2091
|
+
#
|
1957
2092
|
0 # [0, [
|
1958
2093
|
|- 1 # [1, [[2, []]]],
|
1959
2094
|
| |- 2 # [3, []],
|
@@ -1964,7 +2099,24 @@ Use these data structures to build your own visualization of the transitions.
|
|
1964
2099
|
| |- 7 # [8, []]
|
1965
2100
|
|- 8 # ]]
|
1966
2101
|
|
1967
|
-
#
|
2102
|
+
# level_parent:
|
2103
|
+
# The event logs ids are the keys, and the level (depth) and parent id the values.
|
2104
|
+
# {
|
2105
|
+
0 # 0 => [0, 0],
|
2106
|
+
|- 1 # 1 => [1, 0],
|
2107
|
+
| |- 2 # 2 => [2, 1],
|
2108
|
+
|- 3 # 3 => [1, 0],
|
2109
|
+
|- 4 # 4 => [1, 0],
|
2110
|
+
| |- 5 # 5 => [2, 4],
|
2111
|
+
| |- 6 # 6 => [2, 4],
|
2112
|
+
| |- 7 # 7 => [3, 6],
|
2113
|
+
|- 8 # 8 => [1, 0]
|
2114
|
+
# }
|
2115
|
+
|
2116
|
+
# matrix:
|
2117
|
+
# The rows are the direct blocks from the root block,
|
2118
|
+
# and the columns are the nested blocks from the direct ones.
|
2119
|
+
# {
|
1968
2120
|
0 | 1 | 2 | 3 | 4 # 0 => [0, 0],
|
1969
2121
|
- | - | - | - | - # 1 => [1, 1],
|
1970
2122
|
0 | | | | # 2 => [1, 2],
|
@@ -1983,78 +2135,84 @@ Use these data structures to build your own visualization of the transitions.
|
|
1983
2135
|
|
1984
2136
|
#### Turning on/off
|
1985
2137
|
|
1986
|
-
You can use `BCDD::Result.config.feature.disable!(
|
2138
|
+
You can use `BCDD::Result.config.feature.disable!(event_logs)` and `BCDD::Result.config.feature.enable!(event_logs)` to turn on/off the `BCDD::Result.event_logs` feature.
|
1987
2139
|
|
1988
2140
|
```ruby
|
1989
2141
|
BCDD::Result.configuration do |config|
|
1990
|
-
config.feature.disable!(
|
2142
|
+
config.feature.disable!(event_logs)
|
1991
2143
|
end
|
1992
2144
|
|
1993
2145
|
result = SumDivisionsByTwo.call(20, 10)
|
1994
2146
|
# => #<BCDD::Result::Success type=:sum value=15>
|
1995
2147
|
|
1996
|
-
result.
|
1997
|
-
|
1998
|
-
|
2148
|
+
result.event_logs
|
2149
|
+
{
|
2150
|
+
:version=>1,
|
2151
|
+
:records=>[],
|
2152
|
+
:metadata=>{
|
2153
|
+
:duration=>0,
|
2154
|
+
:ids=>{:tree=>[], :matrix=>{}, :level_parent=>{}}, :trace_id=>nil
|
2155
|
+
}
|
2156
|
+
}
|
1999
2157
|
```
|
2000
2158
|
|
2001
2159
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2002
2160
|
|
2003
2161
|
#### Setting a `trace_id` fetcher
|
2004
2162
|
|
2005
|
-
You can define a lambda (arity 0) to fetch the trace_id. This lambda will be called before the first
|
2163
|
+
You can define a lambda (arity 0) to fetch the trace_id. This lambda will be called before the first event logs block and will be used to set the `:trace_id` in the `:metadata` property.
|
2006
2164
|
|
2007
2165
|
Use to correlate different or the same operation (executed multiple times).
|
2008
2166
|
|
2009
2167
|
```ruby
|
2010
|
-
BCDD::Result.config.
|
2168
|
+
BCDD::Result.config.event_logs.trace_id = -> { Thread.current[:bcdd_result_event_logs_trace_id] }
|
2011
2169
|
```
|
2012
2170
|
|
2013
2171
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2014
2172
|
|
2015
2173
|
#### Setting a `listener`
|
2016
2174
|
|
2017
|
-
You can define a listener to be called during the
|
2175
|
+
You can define a listener to be called during the event logs tracking (check out [this example](examples/single_listener/lib/single_event_logs_listener.rb)). It must be a class that includes `BCDD::Result::EventLogs::Listener`.
|
2018
2176
|
|
2019
|
-
Use it to build your additional logic on top of the
|
2020
|
-
- Log the
|
2021
|
-
- Perform
|
2022
|
-
- Instrument the
|
2023
|
-
- Build a visualization
|
2177
|
+
Use it to build your additional logic on top of the tracking. Examples:
|
2178
|
+
- Log the event logs.
|
2179
|
+
- Perform the tracing.
|
2180
|
+
- Instrument the event logs (measure/report).
|
2181
|
+
- Build a visualization (Diagrams, using the `records:` + `metadata: {ids:}` properties).
|
2024
2182
|
|
2025
|
-
After implementing your listener, you can set it to the `BCDD::Result.config.
|
2183
|
+
After implementing your listener, you can set it to the `BCDD::Result.config.event_logs.listener=`:
|
2026
2184
|
|
2027
2185
|
```ruby
|
2028
|
-
BCDD::Result.config.
|
2186
|
+
BCDD::Result.config.event_logs.listener = MyEventLogsListener
|
2029
2187
|
```
|
2030
2188
|
|
2031
2189
|
See the example below to understand how to implement one:
|
2032
2190
|
|
2033
2191
|
```ruby
|
2034
|
-
class
|
2035
|
-
include BCDD::Result::
|
2192
|
+
class MyEventLogsListener
|
2193
|
+
include BCDD::Result::EventLogs::Listener
|
2036
2194
|
|
2037
|
-
# A listener will be initialized before the first
|
2195
|
+
# A listener will be initialized before the first event logs block, and it is discarded after the last one.
|
2038
2196
|
def initialize
|
2039
2197
|
end
|
2040
2198
|
|
2041
|
-
# This method will be called before each
|
2042
|
-
# The parent
|
2199
|
+
# This method will be called before each event logs block.
|
2200
|
+
# The parent block will be called first in the case of nested ones.
|
2043
2201
|
#
|
2044
2202
|
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2045
2203
|
def on_start(scope:)
|
2046
2204
|
end
|
2047
2205
|
|
2048
|
-
# This method will wrap all the
|
2049
|
-
# It can be used to perform an instrumentation (measure/report)
|
2206
|
+
# This method will wrap all the event logs in the same block.
|
2207
|
+
# It can be used to perform an instrumentation (measure/report).
|
2050
2208
|
#
|
2051
2209
|
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2052
|
-
def
|
2210
|
+
def around_event_logs(scope:)
|
2053
2211
|
yield
|
2054
2212
|
end
|
2055
2213
|
|
2056
2214
|
# This method will wrap each and_then call.
|
2057
|
-
# It can be used to perform an instrumentation
|
2215
|
+
# It can be used to perform an instrumentation of the and_then calls.
|
2058
2216
|
#
|
2059
2217
|
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2060
2218
|
# @param and_then:
|
@@ -2078,29 +2236,32 @@ class MyTransitionsListener
|
|
2078
2236
|
def on_record(record:)
|
2079
2237
|
end
|
2080
2238
|
|
2081
|
-
# This method will be called at the end of the
|
2239
|
+
# This method will be called at the end of the event logs tracking.
|
2082
2240
|
#
|
2083
|
-
# @param
|
2241
|
+
# @param event_logs:
|
2084
2242
|
# {
|
2085
2243
|
# :version => 1,
|
2086
2244
|
# :metadata => {
|
2087
2245
|
# :duration => 0,
|
2088
2246
|
# :trace_id => nil,
|
2089
|
-
# :
|
2090
|
-
#
|
2247
|
+
# :ids => {
|
2248
|
+
# :tree => [0, [[1, []], [2, []]]],
|
2249
|
+
# :matrix => { 0 => [0, 0], 1 => [1, 1], 2 => [2, 1]},
|
2250
|
+
# :level_parent => { 0 => [0, 0], 1 => [1, 0], 2 => [1, 0]}
|
2251
|
+
# }
|
2091
2252
|
# },
|
2092
2253
|
# :records => [
|
2093
2254
|
# # ...
|
2094
2255
|
# ]
|
2095
2256
|
# }
|
2096
|
-
def on_finish(
|
2257
|
+
def on_finish(event_logs:)
|
2097
2258
|
end
|
2098
2259
|
|
2099
|
-
# This method will be called when an exception is raised during the
|
2260
|
+
# This method will be called when an exception is raised during the event logs tracking.
|
2100
2261
|
#
|
2101
2262
|
# @param exception: Exception
|
2102
|
-
# @param
|
2103
|
-
def before_interruption(exception:,
|
2263
|
+
# @param event_logs: Hash
|
2264
|
+
def before_interruption(exception:, event_logs:)
|
2104
2265
|
end
|
2105
2266
|
end
|
2106
2267
|
```
|
@@ -2109,15 +2270,15 @@ end
|
|
2109
2270
|
|
2110
2271
|
#### Setting multiple `listeners`
|
2111
2272
|
|
2112
|
-
You can use `BCDD::Result::
|
2273
|
+
You can use `BCDD::Result::EventLogs::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
2274
|
|
2114
|
-
**Attention:** It only allows one listener to handle `around_and_then` and another `
|
2275
|
+
**Attention:** It only allows one listener to handle `around_and_then` and another `around_event_logs` records.
|
2115
2276
|
|
2116
|
-
> The example below defines different listeners to handle `around_and_then` and `
|
2277
|
+
> The example below defines different listeners to handle `around_and_then` and `around_event_logs,` but it is also possible to define a listener to handle both.
|
2117
2278
|
|
2118
2279
|
```ruby
|
2119
2280
|
class AroundAndThenListener
|
2120
|
-
include BCDD::Result::
|
2281
|
+
include BCDD::Result::EventLogs::Listener
|
2121
2282
|
|
2122
2283
|
# It must be a static/singleton method.
|
2123
2284
|
def self.around_and_then?
|
@@ -2129,21 +2290,21 @@ class AroundAndThenListener
|
|
2129
2290
|
end
|
2130
2291
|
end
|
2131
2292
|
|
2132
|
-
class
|
2133
|
-
include BCDD::Result::
|
2293
|
+
class AroundEventLogsListener
|
2294
|
+
include BCDD::Result::EventLogs::Listener
|
2134
2295
|
|
2135
2296
|
# It must be a static/singleton method.
|
2136
|
-
def self.
|
2297
|
+
def self.around_event_logs?
|
2137
2298
|
true
|
2138
2299
|
end
|
2139
2300
|
|
2140
|
-
def
|
2301
|
+
def around_event_logs(scope:)
|
2141
2302
|
#...
|
2142
2303
|
end
|
2143
2304
|
end
|
2144
2305
|
|
2145
|
-
class
|
2146
|
-
include BCDD::Result::
|
2306
|
+
class MyEventLogsListener
|
2307
|
+
include BCDD::Result::EventLogs::Listener
|
2147
2308
|
end
|
2148
2309
|
```
|
2149
2310
|
|
@@ -2151,20 +2312,20 @@ How to use it:
|
|
2151
2312
|
|
2152
2313
|
```ruby
|
2153
2314
|
# The listeners will be called in the order they were added.
|
2154
|
-
BCDD::Result.config.
|
2155
|
-
|
2315
|
+
BCDD::Result.config.event_logs.listener = BCDD::Result::EventLogs::Listeners[
|
2316
|
+
MyEventLogsListener,
|
2156
2317
|
AroundAndThenListener,
|
2157
|
-
|
2318
|
+
AroundEventLogsListener
|
2158
2319
|
]
|
2159
2320
|
```
|
2160
2321
|
|
2161
|
-
> Check out [this example](examples/multiple_listeners) to see a listener to print the
|
2322
|
+
> Check out [this example](examples/multiple_listeners) to see a listener to print the event logs and another to store them in the database.
|
2162
2323
|
|
2163
2324
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2164
2325
|
|
2165
2326
|
## `BCDD::Result.configuration`
|
2166
2327
|
|
2167
|
-
The `BCDD::Result.configuration` allows you to configure default behaviors for `BCDD::Result` and `BCDD::
|
2328
|
+
The `BCDD::Result.configuration` allows you to configure default behaviors for `BCDD::Result` and `BCDD::Context` through a configuration block. After using it, the configuration is frozen, ensuring the expected behaviors for your application.
|
2168
2329
|
|
2169
2330
|
> 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
2331
|
|
@@ -2186,13 +2347,13 @@ Let's see what each configuration in the example above does:
|
|
2186
2347
|
|
2187
2348
|
### `config.addon.enable!(:given, :continue)` <!-- omit in toc -->
|
2188
2349
|
|
2189
|
-
This configuration enables the `Continue()` method for `BCDD::Result.mixin`, `BCDD::
|
2350
|
+
This configuration enables the `Continue()` method for `BCDD::Result.mixin`, `BCDD::Context.mixin`, `BCDD::Result::Expectation.mixin`, and `BCDD::Context::Expectation.mixin`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).
|
2190
2351
|
|
2191
2352
|
It is also enabling the `Given()` which is already enabled by default. Link to documentation: [(1)](#add-ons) [(2)](#mixin-add-ons).
|
2192
2353
|
|
2193
2354
|
### `config.constant_alias.enable!('Result', 'BCDD::Context')` <!-- omit in toc -->
|
2194
2355
|
|
2195
|
-
This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::
|
2356
|
+
This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::Context`.
|
2196
2357
|
|
2197
2358
|
Link to documentations:
|
2198
2359
|
- [Result alias](#bcddresult-versus-result)
|
@@ -2200,11 +2361,11 @@ Link to documentations:
|
|
2200
2361
|
|
2201
2362
|
### `config.pattern_matching.disable!(:nil_as_valid_value_checking)` <!-- omit in toc -->
|
2202
2363
|
|
2203
|
-
This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::
|
2364
|
+
This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::Context`. Link to [documentation](#pattern-matching-support).
|
2204
2365
|
|
2205
2366
|
### `config.feature.disable!(:expectations)` <!-- omit in toc -->
|
2206
2367
|
|
2207
|
-
This configuration turns off the expectations for `BCDD::Result` and `BCDD::
|
2368
|
+
This configuration turns off the expectations for `BCDD::Result` and `BCDD::Context`. The expectations are helpful in development and test environments, but they can be disabled in production environments for performance gain.
|
2208
2369
|
|
2209
2370
|
PS: I'm using `::Rails.env.production?` to check the environment, but you can use any logic you want.
|
2210
2371
|
|
@@ -2226,18 +2387,18 @@ BCDD::Result.config.addon.options
|
|
2226
2387
|
# :enabled=>false,
|
2227
2388
|
# :affects=>[
|
2228
2389
|
# "BCDD::Result.mixin",
|
2229
|
-
# "BCDD::
|
2390
|
+
# "BCDD::Context.mixin",
|
2230
2391
|
# "BCDD::Result::Expectations.mixin",
|
2231
|
-
# "BCDD::
|
2392
|
+
# "BCDD::Context::Expectations.mixin"
|
2232
2393
|
# ]
|
2233
2394
|
# },
|
2234
2395
|
# :given=>{
|
2235
2396
|
# :enabled=>true,
|
2236
2397
|
# :affects=>[
|
2237
2398
|
# "BCDD::Result.mixin",
|
2238
|
-
# "BCDD::
|
2399
|
+
# "BCDD::Context.mixin",
|
2239
2400
|
# "BCDD::Result::Expectations.mixin",
|
2240
|
-
# "BCDD::
|
2401
|
+
# "BCDD::Context::Expectations.mixin"
|
2241
2402
|
# ]
|
2242
2403
|
# }
|
2243
2404
|
# }
|
@@ -2269,7 +2430,7 @@ BCDD::Result.config.pattern_matching.options
|
|
2269
2430
|
# :enabled=>false,
|
2270
2431
|
# :affects=>[
|
2271
2432
|
# "BCDD::Result::Expectations,
|
2272
|
-
# "BCDD::
|
2433
|
+
# "BCDD::Context::Expectations"
|
2273
2434
|
# ]
|
2274
2435
|
# }
|
2275
2436
|
# }
|
@@ -2286,21 +2447,21 @@ BCDD::Result.config.feature.options
|
|
2286
2447
|
# :enabled=>true,
|
2287
2448
|
# :affects=>[
|
2288
2449
|
# "BCDD::Result::Expectations,
|
2289
|
-
# "BCDD::
|
2450
|
+
# "BCDD::Context::Expectations"
|
2290
2451
|
# ]
|
2291
2452
|
# },
|
2292
|
-
#
|
2453
|
+
# event_logs=>{
|
2293
2454
|
# :enabled=>true,
|
2294
2455
|
# :affects=>[
|
2295
2456
|
# "BCDD::Result",
|
2296
|
-
# "BCDD::
|
2457
|
+
# "BCDD::Context"
|
2297
2458
|
# ]
|
2298
2459
|
# },
|
2299
2460
|
# :and_then!=>{
|
2300
2461
|
# :enabled=>false,
|
2301
2462
|
# :affects=>[
|
2302
2463
|
# "BCDD::Result",
|
2303
|
-
# "BCDD::
|
2464
|
+
# "BCDD::Context"
|
2304
2465
|
# ]
|
2305
2466
|
# },
|
2306
2467
|
# }
|
@@ -2343,7 +2504,7 @@ class PlaceOrder < Micro::Case
|
|
2343
2504
|
end
|
2344
2505
|
```
|
2345
2506
|
|
2346
|
-
To facilitate migration for users accustomed to the above approaches, `bcdd-result` includes the `BCDD::Result#and_then!`/`BCDD::
|
2507
|
+
To facilitate migration for users accustomed to the above approaches, `bcdd-result` includes the `BCDD::Result#and_then!`/`BCDD::Context#and_then!` methods, which will invoke the method `call` of the given operation and expect it to return a `BCDD::Result`/`BCDD::Context` object.
|
2347
2508
|
|
2348
2509
|
```ruby
|
2349
2510
|
BCDD::Result.configure do |config|
|
@@ -2351,7 +2512,7 @@ BCDD::Result.configure do |config|
|
|
2351
2512
|
end
|
2352
2513
|
|
2353
2514
|
class PlaceOrder
|
2354
|
-
include BCDD::
|
2515
|
+
include BCDD::Context.mixin
|
2355
2516
|
|
2356
2517
|
def call(**input)
|
2357
2518
|
Given(input)
|
@@ -2383,11 +2544,11 @@ class PlaceOrder
|
|
2383
2544
|
end
|
2384
2545
|
```
|
2385
2546
|
|
2386
|
-
**In BCDD::
|
2547
|
+
**In BCDD::Context**
|
2387
2548
|
|
2388
2549
|
```ruby
|
2389
2550
|
class PlaceOrder
|
2390
|
-
include BCDD::
|
2551
|
+
include BCDD::Context.mixin
|
2391
2552
|
|
2392
2553
|
def call(logger:, **input)
|
2393
2554
|
Given(input)
|
@@ -2441,7 +2602,7 @@ Attention: to ensure the correct behavior, do not mix `#and_then` and `#and_then
|
|
2441
2602
|
|
2442
2603
|
#### Analysis: Why is `#and_then` the antidote/standard?
|
2443
2604
|
|
2444
|
-
The `BCDD::Result#and_then`/`BCDD::
|
2605
|
+
The `BCDD::Result#and_then`/`BCDD::Context#and_then` methods diverge from the above approach by requiring explicit invocation and mapping of the outcomes at each process step. This approach has the following advantages:
|
2445
2606
|
|
2446
2607
|
- **Clarity:** The input/output relationship between the steps is apparent and highly understandable.
|
2447
2608
|
|
@@ -2451,7 +2612,7 @@ See this example to understand what your code should look like:
|
|
2451
2612
|
|
2452
2613
|
```ruby
|
2453
2614
|
class PlaceOrder
|
2454
|
-
include BCDD::
|
2615
|
+
include BCDD::Context.mixin(config: { addon: { continue: true } })
|
2455
2616
|
|
2456
2617
|
def call(**input)
|
2457
2618
|
Given(input)
|