bcdd-result 0.13.0 → 1.1.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 +61 -21
- data/README.md +397 -227
- 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 +6 -5
- 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 +11 -11
- data/lib/bcdd/context.rb +115 -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
|
-
- [
|
69
|
-
- [`BCDD::
|
66
|
+
- [Hash methods](#hash-methods)
|
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,61 +1400,70 @@ 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
|
+
#### Hash methods
|
1440
|
+
|
1441
|
+
The `BCDD::Context` only accepts hashes as its values. Because of this, its instances have some Hash's methods to query/access the values. The available methods are:
|
1469
1442
|
|
1470
|
-
|
1443
|
+
- `#slice` to extract only the desired keys.
|
1444
|
+
- `#[]`, `#dig`, `#fetch` to access the values.
|
1445
|
+
- `#values_at` and `#fetch_values` to get the values of the desired keys.
|
1471
1446
|
|
1472
1447
|
```ruby
|
1473
|
-
BCDD::
|
1474
|
-
config.context_alias.enable!('BCDD::Context')
|
1448
|
+
result = BCDD::Context::Success(:ok, a: 1, b: 2, c: {d: 4})
|
1475
1449
|
|
1476
|
-
|
1450
|
+
result[:a] # 1
|
1451
|
+
result.fetch(:a) # 1
|
1452
|
+
result.dig(:c, :d) # 4
|
1477
1453
|
|
1478
|
-
|
1479
|
-
|
1454
|
+
result.slice(:a, :b) # {:a=>1, :b=>2}
|
1455
|
+
|
1456
|
+
result.values_at(:a, :b) # [1, 2]
|
1457
|
+
result.fetch_values(:a, :b) # [1, 2]
|
1480
1458
|
```
|
1481
1459
|
|
1460
|
+
These methods are available for `BCDD::Context::Success` and `BCDD::Context::Failure` instances.
|
1461
|
+
|
1482
1462
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1483
1463
|
|
1484
|
-
#### `BCDD::
|
1464
|
+
#### `BCDD::Context.mixin`
|
1485
1465
|
|
1486
|
-
As in the `BCDD::Result`, you can use the `BCDD::
|
1466
|
+
As in the `BCDD::Result`, you can use the `BCDD::Context.mixin` to add the `Success()` and `Failure()` methods to your classes/modules.
|
1487
1467
|
|
1488
1468
|
Let's see this feature and the data accumulation in action:
|
1489
1469
|
|
@@ -1493,7 +1473,7 @@ Let's see this feature and the data accumulation in action:
|
|
1493
1473
|
require 'logger'
|
1494
1474
|
|
1495
1475
|
class Divide
|
1496
|
-
include BCDD::
|
1476
|
+
include BCDD::Context.mixin
|
1497
1477
|
|
1498
1478
|
def call(arg1, arg2, logger: ::Logger.new(STDOUT))
|
1499
1479
|
validate_numbers(arg1, arg2)
|
@@ -1530,29 +1510,29 @@ end
|
|
1530
1510
|
|
1531
1511
|
Divide.new.call(10, 5)
|
1532
1512
|
# I, [2023-10-27T01:51:46.905004 #76915] INFO -- : The division result is 2
|
1533
|
-
#<BCDD::
|
1513
|
+
#<BCDD::Context::Success type=:ok value={:number=>2}>
|
1534
1514
|
|
1535
1515
|
Divide.new.call('10', 5)
|
1536
|
-
#<BCDD::
|
1516
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg1 must be numeric"}>
|
1537
1517
|
|
1538
1518
|
Divide.new.call(10, '5')
|
1539
|
-
#<BCDD::
|
1519
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must be numeric"}>
|
1540
1520
|
|
1541
1521
|
Divide.new.call(10, 0)
|
1542
|
-
#<BCDD::
|
1522
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must not be zero"}>
|
1543
1523
|
```
|
1544
1524
|
|
1545
1525
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1546
1526
|
|
1547
1527
|
##### `and_expose`
|
1548
1528
|
|
1549
|
-
This allows you to expose only the desired keys from the accumulated result. It can be used with any `BCDD::
|
1529
|
+
This allows you to expose only the desired keys from the accumulated result. It can be used with any `BCDD::Context` object.
|
1550
1530
|
|
1551
1531
|
Let's add it to the previous example:
|
1552
1532
|
|
1553
1533
|
```ruby
|
1554
1534
|
class Divide
|
1555
|
-
include BCDD::
|
1535
|
+
include BCDD::Context.mixin
|
1556
1536
|
|
1557
1537
|
def call(arg1, arg2)
|
1558
1538
|
validate_numbers(arg1, arg2)
|
@@ -1582,7 +1562,7 @@ class Divide
|
|
1582
1562
|
end
|
1583
1563
|
|
1584
1564
|
Divide.new.call(10, 5)
|
1585
|
-
#<BCDD::
|
1565
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1586
1566
|
```
|
1587
1567
|
|
1588
1568
|
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 +1571,7 @@ Remove the `#and_expose` call to see the difference. This will be the outcome:
|
|
1591
1571
|
|
1592
1572
|
```ruby
|
1593
1573
|
Divide.new.call(10, 5)
|
1594
|
-
#<BCDD::
|
1574
|
+
#<BCDD::Context::Success type=:ok value={:number=>2, :number1=>10, :number2=>5}>
|
1595
1575
|
```
|
1596
1576
|
|
1597
1577
|
> 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 +1580,11 @@ Divide.new.call(10, 5)
|
|
1600
1580
|
|
1601
1581
|
##### Module example (Singleton Methods)
|
1602
1582
|
|
1603
|
-
`BCDD::
|
1583
|
+
`BCDD::Context.mixin` can also produce singleton methods. Below is an example using a module (but it could be a class, too).
|
1604
1584
|
|
1605
1585
|
```ruby
|
1606
1586
|
module Divide
|
1607
|
-
extend self, BCDD::
|
1587
|
+
extend self, BCDD::Context.mixin
|
1608
1588
|
|
1609
1589
|
def call(arg1, arg2)
|
1610
1590
|
validate_numbers(arg1, arg2)
|
@@ -1634,29 +1614,29 @@ module Divide
|
|
1634
1614
|
end
|
1635
1615
|
|
1636
1616
|
Divide.call(10, 5)
|
1637
|
-
#<BCDD::
|
1617
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1638
1618
|
|
1639
1619
|
Divide.call('10', 5)
|
1640
|
-
#<BCDD::
|
1620
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg1 must be numeric"}>
|
1641
1621
|
|
1642
1622
|
Divide.call(10, '5')
|
1643
|
-
#<BCDD::
|
1623
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must be numeric"}>
|
1644
1624
|
|
1645
1625
|
Divide.call(10, 0)
|
1646
|
-
#<BCDD::
|
1626
|
+
#<BCDD::Context::Failure type=:err value={:message=>"arg2 must not be zero"}>
|
1647
1627
|
```
|
1648
1628
|
|
1649
1629
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1650
1630
|
|
1651
|
-
#### `BCDD::
|
1631
|
+
#### `BCDD::Context::Expectations`
|
1652
1632
|
|
1653
|
-
The `BCDD::
|
1633
|
+
The `BCDD::Context::Expectations` is a `BCDD::Result::Expectations` with the `BCDD::Context` features.
|
1654
1634
|
|
1655
1635
|
This is an example using the mixin mode, but the standalone mode is also supported.
|
1656
1636
|
|
1657
1637
|
```ruby
|
1658
1638
|
class Divide
|
1659
|
-
include BCDD::
|
1639
|
+
include BCDD::Context::Expectations.mixin(
|
1660
1640
|
config: {
|
1661
1641
|
pattern_matching: { nil_as_valid_value_checking: true }
|
1662
1642
|
},
|
@@ -1680,16 +1660,16 @@ class Divide
|
|
1680
1660
|
end
|
1681
1661
|
|
1682
1662
|
Divide.new.call(10, 5)
|
1683
|
-
#<BCDD::
|
1663
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1684
1664
|
```
|
1685
1665
|
|
1686
|
-
As in the `BCDD::Result::Expectations.mixin`, the `BCDD::
|
1666
|
+
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
1667
|
|
1688
1668
|
Let's see this using the previous example:
|
1689
1669
|
|
1690
1670
|
```ruby
|
1691
1671
|
Divide::Result::Success(:division_completed, number: 2)
|
1692
|
-
#<BCDD::
|
1672
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>2}>
|
1693
1673
|
|
1694
1674
|
Divide::Result::Success(:division_completed, number: '2')
|
1695
1675
|
# 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 +1679,7 @@ Divide::Result::Success(:division_completed, number: '2')
|
|
1699
1679
|
|
1700
1680
|
#### Mixin add-ons
|
1701
1681
|
|
1702
|
-
The `BCDD::
|
1682
|
+
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
1683
|
|
1704
1684
|
**given**
|
1705
1685
|
|
@@ -1707,21 +1687,21 @@ This addon is enabled by default. It will create the `Given(*value)` method. Use
|
|
1707
1687
|
|
1708
1688
|
You can turn it off by passing `given: false` to the `config:` argument or using the `BCDD::Result.configuration`.
|
1709
1689
|
|
1710
|
-
The `Given()` addon for a BCDD::
|
1690
|
+
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
1691
|
|
1712
1692
|
**continue**
|
1713
1693
|
|
1714
|
-
The `BCDD::
|
1694
|
+
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
1695
|
|
1716
1696
|
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
1697
|
|
1718
|
-
Let's use a mix of `BCDD::
|
1698
|
+
Let's use a mix of `BCDD::Context` features to see in action with this add-on:
|
1719
1699
|
|
1720
1700
|
```ruby
|
1721
1701
|
module Division
|
1722
1702
|
require 'logger'
|
1723
1703
|
|
1724
|
-
extend self, BCDD::
|
1704
|
+
extend self, BCDD::Context::Expectations.mixin(
|
1725
1705
|
config: {
|
1726
1706
|
addon: { continue: true },
|
1727
1707
|
pattern_matching: { nil_as_valid_value_checking: true }
|
@@ -1771,35 +1751,193 @@ end
|
|
1771
1751
|
|
1772
1752
|
Division.call(14, 2)
|
1773
1753
|
# I, [2023-10-27T02:01:05.812388 #77823] INFO -- : The division result is 7
|
1774
|
-
#<BCDD::
|
1754
|
+
#<BCDD::Context::Success type=:division_completed value={:number=>7}>
|
1775
1755
|
|
1776
1756
|
Division.call(0, 2)
|
1777
|
-
##<BCDD::
|
1757
|
+
##<BCDD::Context::Success type=:division_completed value={:number=>0}>
|
1778
1758
|
|
1779
1759
|
Division.call('14', 2)
|
1780
|
-
#<BCDD::
|
1760
|
+
#<BCDD::Context::Failure type=:invalid_arg value={:message=>"arg1 must be numeric"}>
|
1781
1761
|
|
1782
1762
|
Division.call(14, '2')
|
1783
|
-
#<BCDD::
|
1763
|
+
#<BCDD::Context::Failure type=:invalid_arg value={:message=>"arg2 must be numeric"}>
|
1784
1764
|
|
1785
1765
|
Division.call(14, 0)
|
1786
|
-
#<BCDD::
|
1766
|
+
#<BCDD::Context::Failure type=:division_by_zero value={:message=>"arg2 must not be zero"}>
|
1787
1767
|
```
|
1788
1768
|
|
1789
1769
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1790
1770
|
|
1791
|
-
##
|
1771
|
+
## Pattern Matching
|
1792
1772
|
|
1793
|
-
|
1773
|
+
The `BCDD::Result` and `BCDD::Context` also provides support to pattern matching.
|
1794
1774
|
|
1795
|
-
|
1775
|
+
### `BCDD::Result`
|
1776
|
+
|
1777
|
+
In the further examples, I will use the `Divide` lambda to exemplify its usage.
|
1778
|
+
|
1779
|
+
```ruby
|
1780
|
+
Divide = lambda do |arg1, arg2|
|
1781
|
+
arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric')
|
1782
|
+
arg2.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg2 must be numeric')
|
1783
|
+
|
1784
|
+
return BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?
|
1785
|
+
|
1786
|
+
BCDD::Result::Success(:division_completed, arg1 / arg2)
|
1787
|
+
end
|
1788
|
+
```
|
1789
|
+
|
1790
|
+
#### `Array`/`Find` patterns
|
1791
|
+
|
1792
|
+
```ruby
|
1793
|
+
case Divide.call(4, 2)
|
1794
|
+
in BCDD::Failure[:invalid_arg, msg] then puts msg
|
1795
|
+
in BCDD::Failure[:division_by_zero, msg] then puts msg
|
1796
|
+
in BCDD::Success[:division_completed, num] then puts num
|
1797
|
+
end
|
1798
|
+
|
1799
|
+
# The code above will print: 2
|
1800
|
+
|
1801
|
+
case Divide.call(4, 0)
|
1802
|
+
in BCDD::Failure[:invalid_arg, msg] then puts msg
|
1803
|
+
in BCDD::Failure[:division_by_zero, msg] then puts msg
|
1804
|
+
in BCDD::Success[:division_completed, num] then puts num
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
# The code above will print: arg2 must not be zero
|
1808
|
+
```
|
1809
|
+
|
1810
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1811
|
+
|
1812
|
+
#### `Hash` patterns
|
1813
|
+
|
1814
|
+
```ruby
|
1815
|
+
case Divide.call(10, 2)
|
1816
|
+
in BCDD::Failure(type: :invalid_arg, value: msg) then puts msg
|
1817
|
+
in BCDD::Failure(type: :division_by_zero, value: msg) then puts msg
|
1818
|
+
in BCDD::Success(type: :division_completed, value: num) then puts num
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
# The code above will print: 5
|
1822
|
+
|
1823
|
+
case Divide.call('10', 2)
|
1824
|
+
in BCDD::Failure(type: :invalid_arg, value: msg) then puts msg
|
1825
|
+
in BCDD::Failure(type: :division_by_zero, value: msg) then puts msg
|
1826
|
+
in BCDD::Success(type: :division_completed, value: num) then puts num
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
# The code above will print: arg1 must be numeric
|
1830
|
+
```
|
1831
|
+
|
1832
|
+
You can also use `BCDD::Result::Success` and `BCDD::Result::Failure` as patterns.
|
1833
|
+
|
1834
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1835
|
+
|
1836
|
+
|
1837
|
+
### `BCDD::Context`
|
1838
|
+
|
1839
|
+
In the further examples, I will use the `Divide` lambda to exemplify its usage.
|
1840
|
+
|
1841
|
+
```ruby
|
1842
|
+
Divide = lambda do |arg1, arg2|
|
1843
|
+
arg1.is_a?(::Numeric) or return BCDD::Context::Failure(:invalid_arg, err: 'arg1 must be numeric')
|
1844
|
+
arg2.is_a?(::Numeric) or return BCDD::Context::Failure(:invalid_arg, err: 'arg2 must be numeric')
|
1845
|
+
|
1846
|
+
return BCDD::Context::Failure(:division_by_zero, err: 'arg2 must not be zero') if arg2.zero?
|
1847
|
+
|
1848
|
+
BCDD::Context::Success(:division_completed, num: arg1 / arg2)
|
1849
|
+
end
|
1850
|
+
```
|
1851
|
+
|
1852
|
+
#### `Array`/`Find` patterns
|
1853
|
+
|
1854
|
+
```ruby
|
1855
|
+
case Divide.call(4, 2)
|
1856
|
+
in BCDD::Failure[:invalid_arg, {msg:}] then puts msg
|
1857
|
+
in BCDD::Failure[:division_by_zero, {msg:}] then puts msg
|
1858
|
+
in BCDD::Success[:division_completed, {num:}] then puts num
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
# The code above will print: 2
|
1862
|
+
|
1863
|
+
case Divide.call(4, 0)
|
1864
|
+
in BCDD::Failure[:invalid_arg, {msg:}] then puts msg
|
1865
|
+
in BCDD::Failure[:division_by_zero, {msg:}] then puts msg
|
1866
|
+
in BCDD::Success[:division_completed, {num:}] then puts num
|
1867
|
+
end
|
1868
|
+
|
1869
|
+
# The code above will print: arg2 must not be zero
|
1870
|
+
```
|
1871
|
+
|
1872
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1873
|
+
|
1874
|
+
#### `Hash` patterns
|
1875
|
+
|
1876
|
+
If you don't provide the keys :type and :value, the pattern will match the result value.
|
1877
|
+
|
1878
|
+
```ruby
|
1879
|
+
case Divide.call(10, 2)
|
1880
|
+
in BCDD::Failure({msg:}) then puts msg
|
1881
|
+
in BCDD::Success({num:}) then puts num
|
1882
|
+
end
|
1883
|
+
```
|
1884
|
+
|
1885
|
+
```ruby
|
1886
|
+
case Divide.call(10, 2)
|
1887
|
+
in BCDD::Failure(type: :invalid_arg, value: {msg:}) then puts msg
|
1888
|
+
in BCDD::Failure(type: :division_by_zero, value: {msg:}) then puts msg
|
1889
|
+
in BCDD::Success(type: :division_completed, value: {num:}) then puts num
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
# The code above will print: 5
|
1893
|
+
|
1894
|
+
case Divide.call('10', 2)
|
1895
|
+
in BCDD::Failure(type: :invalid_arg, value: {msg:}) then puts {msg:}
|
1896
|
+
in BCDD::Failure(type: :division_by_zero, value: {msg:}) then puts msg
|
1897
|
+
in BCDD::Success(type: :division_completed, value: {num:}) then puts num
|
1898
|
+
end
|
1899
|
+
|
1900
|
+
# The code above will print: arg1 must be numeric
|
1901
|
+
```
|
1902
|
+
|
1903
|
+
You can also use `BCDD::Context::Success` and `BCDD::Context::Failure` as patterns.
|
1904
|
+
|
1905
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1906
|
+
|
1907
|
+
### How to pattern match without the concept of success and failure
|
1908
|
+
|
1909
|
+
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.
|
1910
|
+
|
1911
|
+
```ruby
|
1912
|
+
case Divide.call(10, 2)
|
1913
|
+
in BCDD::Context(:invalid_arg, {msg:}) then puts msg
|
1914
|
+
in BCDD::Context(:division_by_zero, {msg:}) then puts msg
|
1915
|
+
in BCDD::Context(:division_completed, {num:}) then puts num
|
1916
|
+
end
|
1917
|
+
|
1918
|
+
case Divide.call(10, 2)
|
1919
|
+
in BCDD::Result(:invalid_arg, msg) then puts msg
|
1920
|
+
in BCDD::Result(:division_by_zero, msg) then puts msg
|
1921
|
+
in BCDD::Result(:division_completed, num) then puts num
|
1922
|
+
end
|
1923
|
+
```
|
1924
|
+
|
1925
|
+
The `BCDD::Result` will also work with the `BCDD::Context`, but the opposite won't.
|
1926
|
+
|
1927
|
+
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1928
|
+
|
1929
|
+
## `BCDD::Result.event_logs`
|
1930
|
+
|
1931
|
+
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.
|
1932
|
+
|
1933
|
+
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
1934
|
|
1797
1935
|
```ruby
|
1798
1936
|
class Division
|
1799
1937
|
include BCDD::Result.mixin(config: { addon: { continue: true } })
|
1800
1938
|
|
1801
1939
|
def call(arg1, arg2)
|
1802
|
-
BCDD::Result.
|
1940
|
+
BCDD::Result.event_logs(name: 'Division', desc: 'divide two numbers') do
|
1803
1941
|
Given([arg1, arg2])
|
1804
1942
|
.and_then(:require_numbers)
|
1805
1943
|
.and_then(:check_for_zeros)
|
@@ -1835,7 +1973,7 @@ module SumDivisionsByTwo
|
|
1835
1973
|
extend self, BCDD::Result.mixin
|
1836
1974
|
|
1837
1975
|
def call(*numbers)
|
1838
|
-
BCDD::Result.
|
1976
|
+
BCDD::Result.event_logs(name: 'SumDivisionsByTwo') do
|
1839
1977
|
divisions = numbers.map { |number| Division.new.call(number, 2) }
|
1840
1978
|
|
1841
1979
|
if divisions.any?(&:failure?)
|
@@ -1854,14 +1992,17 @@ Let's see the result of the `SumDivisionsByTwo` call:
|
|
1854
1992
|
result = SumDivisionsByTwo.call(20, 10)
|
1855
1993
|
# => #<BCDD::Result::Success type=:sum value=15>
|
1856
1994
|
|
1857
|
-
result.
|
1995
|
+
result.event_logs
|
1858
1996
|
{
|
1859
1997
|
:version => 1,
|
1860
1998
|
:metadata => {
|
1861
1999
|
:duration => 0, # milliseconds
|
1862
2000
|
:trace_id => nil, # can be set through configuration
|
1863
|
-
:
|
1864
|
-
|
2001
|
+
:ids => {
|
2002
|
+
:tree => [0, [[1, []], [2, []]]],
|
2003
|
+
:matrix => { 0 => [0, 0], 1 => [1, 1], 2 => [2, 1]},
|
2004
|
+
:level_parent => { 0 => [0, 0], 1 => [1, 0], 2 => [1, 0]}
|
2005
|
+
}
|
1865
2006
|
},
|
1866
2007
|
:records=> [
|
1867
2008
|
{
|
@@ -1942,18 +2083,21 @@ result.transitions
|
|
1942
2083
|
|
1943
2084
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
1944
2085
|
|
1945
|
-
### `
|
1946
|
-
|
1947
|
-
The `:ids_matrix`. It is a simplification of the `:ids_tree` property (a graph/tree representation of the transitions ids).
|
2086
|
+
### `metadata: {ids:}`
|
1948
2087
|
|
1949
|
-
The
|
2088
|
+
The `:ids` metadata property is a hash with three properties:
|
2089
|
+
- `:tree`, a graph/tree representation of the id of each `event_logs` block.
|
2090
|
+
- `:level_parent`, a hash with the level (depth) of each block and its parent id.
|
2091
|
+
- `:matrix`, a matrix representation of the event logs ids. It is a simplification of the `:tree` property.
|
1950
2092
|
|
1951
|
-
Use these data structures to build your own visualization
|
2093
|
+
Use these data structures to build your own visualization.
|
1952
2094
|
|
1953
|
-
> Check out [
|
2095
|
+
> 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
2096
|
|
1955
2097
|
```ruby
|
1956
|
-
#
|
2098
|
+
# tree:
|
2099
|
+
# A graph representation (array of arrays) of the each event logs block id.
|
2100
|
+
#
|
1957
2101
|
0 # [0, [
|
1958
2102
|
|- 1 # [1, [[2, []]]],
|
1959
2103
|
| |- 2 # [3, []],
|
@@ -1964,7 +2108,24 @@ Use these data structures to build your own visualization of the transitions.
|
|
1964
2108
|
| |- 7 # [8, []]
|
1965
2109
|
|- 8 # ]]
|
1966
2110
|
|
1967
|
-
#
|
2111
|
+
# level_parent:
|
2112
|
+
# The event logs ids are the keys, and the level (depth) and parent id the values.
|
2113
|
+
# {
|
2114
|
+
0 # 0 => [0, 0],
|
2115
|
+
|- 1 # 1 => [1, 0],
|
2116
|
+
| |- 2 # 2 => [2, 1],
|
2117
|
+
|- 3 # 3 => [1, 0],
|
2118
|
+
|- 4 # 4 => [1, 0],
|
2119
|
+
| |- 5 # 5 => [2, 4],
|
2120
|
+
| |- 6 # 6 => [2, 4],
|
2121
|
+
| |- 7 # 7 => [3, 6],
|
2122
|
+
|- 8 # 8 => [1, 0]
|
2123
|
+
# }
|
2124
|
+
|
2125
|
+
# matrix:
|
2126
|
+
# The rows are the direct blocks from the root block,
|
2127
|
+
# and the columns are the nested blocks from the direct ones.
|
2128
|
+
# {
|
1968
2129
|
0 | 1 | 2 | 3 | 4 # 0 => [0, 0],
|
1969
2130
|
- | - | - | - | - # 1 => [1, 1],
|
1970
2131
|
0 | | | | # 2 => [1, 2],
|
@@ -1983,78 +2144,84 @@ Use these data structures to build your own visualization of the transitions.
|
|
1983
2144
|
|
1984
2145
|
#### Turning on/off
|
1985
2146
|
|
1986
|
-
You can use `BCDD::Result.config.feature.disable!(
|
2147
|
+
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
2148
|
|
1988
2149
|
```ruby
|
1989
2150
|
BCDD::Result.configuration do |config|
|
1990
|
-
config.feature.disable!(
|
2151
|
+
config.feature.disable!(event_logs)
|
1991
2152
|
end
|
1992
2153
|
|
1993
2154
|
result = SumDivisionsByTwo.call(20, 10)
|
1994
2155
|
# => #<BCDD::Result::Success type=:sum value=15>
|
1995
2156
|
|
1996
|
-
result.
|
1997
|
-
|
1998
|
-
|
2157
|
+
result.event_logs
|
2158
|
+
{
|
2159
|
+
:version=>1,
|
2160
|
+
:records=>[],
|
2161
|
+
:metadata=>{
|
2162
|
+
:duration=>0,
|
2163
|
+
:ids=>{:tree=>[], :matrix=>{}, :level_parent=>{}}, :trace_id=>nil
|
2164
|
+
}
|
2165
|
+
}
|
1999
2166
|
```
|
2000
2167
|
|
2001
2168
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2002
2169
|
|
2003
2170
|
#### Setting a `trace_id` fetcher
|
2004
2171
|
|
2005
|
-
You can define a lambda (arity 0) to fetch the trace_id. This lambda will be called before the first
|
2172
|
+
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
2173
|
|
2007
2174
|
Use to correlate different or the same operation (executed multiple times).
|
2008
2175
|
|
2009
2176
|
```ruby
|
2010
|
-
BCDD::Result.config.
|
2177
|
+
BCDD::Result.config.event_logs.trace_id = -> { Thread.current[:bcdd_result_event_logs_trace_id] }
|
2011
2178
|
```
|
2012
2179
|
|
2013
2180
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2014
2181
|
|
2015
2182
|
#### Setting a `listener`
|
2016
2183
|
|
2017
|
-
You can define a listener to be called during the
|
2184
|
+
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
2185
|
|
2019
|
-
Use it to build your additional logic on top of the
|
2020
|
-
- Log the
|
2021
|
-
- Perform
|
2022
|
-
- Instrument the
|
2023
|
-
- Build a visualization
|
2186
|
+
Use it to build your additional logic on top of the tracking. Examples:
|
2187
|
+
- Log the event logs.
|
2188
|
+
- Perform the tracing.
|
2189
|
+
- Instrument the event logs (measure/report).
|
2190
|
+
- Build a visualization (Diagrams, using the `records:` + `metadata: {ids:}` properties).
|
2024
2191
|
|
2025
|
-
After implementing your listener, you can set it to the `BCDD::Result.config.
|
2192
|
+
After implementing your listener, you can set it to the `BCDD::Result.config.event_logs.listener=`:
|
2026
2193
|
|
2027
2194
|
```ruby
|
2028
|
-
BCDD::Result.config.
|
2195
|
+
BCDD::Result.config.event_logs.listener = MyEventLogsListener
|
2029
2196
|
```
|
2030
2197
|
|
2031
2198
|
See the example below to understand how to implement one:
|
2032
2199
|
|
2033
2200
|
```ruby
|
2034
|
-
class
|
2035
|
-
include BCDD::Result::
|
2201
|
+
class MyEventLogsListener
|
2202
|
+
include BCDD::Result::EventLogs::Listener
|
2036
2203
|
|
2037
|
-
# A listener will be initialized before the first
|
2204
|
+
# A listener will be initialized before the first event logs block, and it is discarded after the last one.
|
2038
2205
|
def initialize
|
2039
2206
|
end
|
2040
2207
|
|
2041
|
-
# This method will be called before each
|
2042
|
-
# The parent
|
2208
|
+
# This method will be called before each event logs block.
|
2209
|
+
# The parent block will be called first in the case of nested ones.
|
2043
2210
|
#
|
2044
2211
|
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2045
2212
|
def on_start(scope:)
|
2046
2213
|
end
|
2047
2214
|
|
2048
|
-
# This method will wrap all the
|
2049
|
-
# It can be used to perform an instrumentation (measure/report)
|
2215
|
+
# This method will wrap all the event logs in the same block.
|
2216
|
+
# It can be used to perform an instrumentation (measure/report).
|
2050
2217
|
#
|
2051
2218
|
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2052
|
-
def
|
2219
|
+
def around_event_logs(scope:)
|
2053
2220
|
yield
|
2054
2221
|
end
|
2055
2222
|
|
2056
2223
|
# This method will wrap each and_then call.
|
2057
|
-
# It can be used to perform an instrumentation
|
2224
|
+
# It can be used to perform an instrumentation of the and_then calls.
|
2058
2225
|
#
|
2059
2226
|
# @param scope: {:id=>1, :name=>"SomeOperation", :desc=>"Optional description"}
|
2060
2227
|
# @param and_then:
|
@@ -2078,29 +2245,32 @@ class MyTransitionsListener
|
|
2078
2245
|
def on_record(record:)
|
2079
2246
|
end
|
2080
2247
|
|
2081
|
-
# This method will be called at the end of the
|
2248
|
+
# This method will be called at the end of the event logs tracking.
|
2082
2249
|
#
|
2083
|
-
# @param
|
2250
|
+
# @param event_logs:
|
2084
2251
|
# {
|
2085
2252
|
# :version => 1,
|
2086
2253
|
# :metadata => {
|
2087
2254
|
# :duration => 0,
|
2088
2255
|
# :trace_id => nil,
|
2089
|
-
# :
|
2090
|
-
#
|
2256
|
+
# :ids => {
|
2257
|
+
# :tree => [0, [[1, []], [2, []]]],
|
2258
|
+
# :matrix => { 0 => [0, 0], 1 => [1, 1], 2 => [2, 1]},
|
2259
|
+
# :level_parent => { 0 => [0, 0], 1 => [1, 0], 2 => [1, 0]}
|
2260
|
+
# }
|
2091
2261
|
# },
|
2092
2262
|
# :records => [
|
2093
2263
|
# # ...
|
2094
2264
|
# ]
|
2095
2265
|
# }
|
2096
|
-
def on_finish(
|
2266
|
+
def on_finish(event_logs:)
|
2097
2267
|
end
|
2098
2268
|
|
2099
|
-
# This method will be called when an exception is raised during the
|
2269
|
+
# This method will be called when an exception is raised during the event logs tracking.
|
2100
2270
|
#
|
2101
2271
|
# @param exception: Exception
|
2102
|
-
# @param
|
2103
|
-
def before_interruption(exception:,
|
2272
|
+
# @param event_logs: Hash
|
2273
|
+
def before_interruption(exception:, event_logs:)
|
2104
2274
|
end
|
2105
2275
|
end
|
2106
2276
|
```
|
@@ -2109,15 +2279,15 @@ end
|
|
2109
2279
|
|
2110
2280
|
#### Setting multiple `listeners`
|
2111
2281
|
|
2112
|
-
You can use `BCDD::Result::
|
2282
|
+
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
2283
|
|
2114
|
-
**Attention:** It only allows one listener to handle `around_and_then` and another `
|
2284
|
+
**Attention:** It only allows one listener to handle `around_and_then` and another `around_event_logs` records.
|
2115
2285
|
|
2116
|
-
> The example below defines different listeners to handle `around_and_then` and `
|
2286
|
+
> 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
2287
|
|
2118
2288
|
```ruby
|
2119
2289
|
class AroundAndThenListener
|
2120
|
-
include BCDD::Result::
|
2290
|
+
include BCDD::Result::EventLogs::Listener
|
2121
2291
|
|
2122
2292
|
# It must be a static/singleton method.
|
2123
2293
|
def self.around_and_then?
|
@@ -2129,21 +2299,21 @@ class AroundAndThenListener
|
|
2129
2299
|
end
|
2130
2300
|
end
|
2131
2301
|
|
2132
|
-
class
|
2133
|
-
include BCDD::Result::
|
2302
|
+
class AroundEventLogsListener
|
2303
|
+
include BCDD::Result::EventLogs::Listener
|
2134
2304
|
|
2135
2305
|
# It must be a static/singleton method.
|
2136
|
-
def self.
|
2306
|
+
def self.around_event_logs?
|
2137
2307
|
true
|
2138
2308
|
end
|
2139
2309
|
|
2140
|
-
def
|
2310
|
+
def around_event_logs(scope:)
|
2141
2311
|
#...
|
2142
2312
|
end
|
2143
2313
|
end
|
2144
2314
|
|
2145
|
-
class
|
2146
|
-
include BCDD::Result::
|
2315
|
+
class MyEventLogsListener
|
2316
|
+
include BCDD::Result::EventLogs::Listener
|
2147
2317
|
end
|
2148
2318
|
```
|
2149
2319
|
|
@@ -2151,20 +2321,20 @@ How to use it:
|
|
2151
2321
|
|
2152
2322
|
```ruby
|
2153
2323
|
# The listeners will be called in the order they were added.
|
2154
|
-
BCDD::Result.config.
|
2155
|
-
|
2324
|
+
BCDD::Result.config.event_logs.listener = BCDD::Result::EventLogs::Listeners[
|
2325
|
+
MyEventLogsListener,
|
2156
2326
|
AroundAndThenListener,
|
2157
|
-
|
2327
|
+
AroundEventLogsListener
|
2158
2328
|
]
|
2159
2329
|
```
|
2160
2330
|
|
2161
|
-
> Check out [this example](examples/multiple_listeners) to see a listener to print the
|
2331
|
+
> 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
2332
|
|
2163
2333
|
<p align="right"><a href="#-bcddresult">⬆️ back to top</a></p>
|
2164
2334
|
|
2165
2335
|
## `BCDD::Result.configuration`
|
2166
2336
|
|
2167
|
-
The `BCDD::Result.configuration` allows you to configure default behaviors for `BCDD::Result` and `BCDD::
|
2337
|
+
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
2338
|
|
2169
2339
|
> 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
2340
|
|
@@ -2186,13 +2356,13 @@ Let's see what each configuration in the example above does:
|
|
2186
2356
|
|
2187
2357
|
### `config.addon.enable!(:given, :continue)` <!-- omit in toc -->
|
2188
2358
|
|
2189
|
-
This configuration enables the `Continue()` method for `BCDD::Result.mixin`, `BCDD::
|
2359
|
+
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
2360
|
|
2191
2361
|
It is also enabling the `Given()` which is already enabled by default. Link to documentation: [(1)](#add-ons) [(2)](#mixin-add-ons).
|
2192
2362
|
|
2193
2363
|
### `config.constant_alias.enable!('Result', 'BCDD::Context')` <!-- omit in toc -->
|
2194
2364
|
|
2195
|
-
This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::
|
2365
|
+
This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::Context`.
|
2196
2366
|
|
2197
2367
|
Link to documentations:
|
2198
2368
|
- [Result alias](#bcddresult-versus-result)
|
@@ -2200,11 +2370,11 @@ Link to documentations:
|
|
2200
2370
|
|
2201
2371
|
### `config.pattern_matching.disable!(:nil_as_valid_value_checking)` <!-- omit in toc -->
|
2202
2372
|
|
2203
|
-
This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::
|
2373
|
+
This configuration disables the `nil_as_valid_value_checking` for `BCDD::Result` and `BCDD::Context`. Link to [documentation](#pattern-matching-support).
|
2204
2374
|
|
2205
2375
|
### `config.feature.disable!(:expectations)` <!-- omit in toc -->
|
2206
2376
|
|
2207
|
-
This configuration turns off the expectations for `BCDD::Result` and `BCDD::
|
2377
|
+
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
2378
|
|
2209
2379
|
PS: I'm using `::Rails.env.production?` to check the environment, but you can use any logic you want.
|
2210
2380
|
|
@@ -2226,18 +2396,18 @@ BCDD::Result.config.addon.options
|
|
2226
2396
|
# :enabled=>false,
|
2227
2397
|
# :affects=>[
|
2228
2398
|
# "BCDD::Result.mixin",
|
2229
|
-
# "BCDD::
|
2399
|
+
# "BCDD::Context.mixin",
|
2230
2400
|
# "BCDD::Result::Expectations.mixin",
|
2231
|
-
# "BCDD::
|
2401
|
+
# "BCDD::Context::Expectations.mixin"
|
2232
2402
|
# ]
|
2233
2403
|
# },
|
2234
2404
|
# :given=>{
|
2235
2405
|
# :enabled=>true,
|
2236
2406
|
# :affects=>[
|
2237
2407
|
# "BCDD::Result.mixin",
|
2238
|
-
# "BCDD::
|
2408
|
+
# "BCDD::Context.mixin",
|
2239
2409
|
# "BCDD::Result::Expectations.mixin",
|
2240
|
-
# "BCDD::
|
2410
|
+
# "BCDD::Context::Expectations.mixin"
|
2241
2411
|
# ]
|
2242
2412
|
# }
|
2243
2413
|
# }
|
@@ -2269,7 +2439,7 @@ BCDD::Result.config.pattern_matching.options
|
|
2269
2439
|
# :enabled=>false,
|
2270
2440
|
# :affects=>[
|
2271
2441
|
# "BCDD::Result::Expectations,
|
2272
|
-
# "BCDD::
|
2442
|
+
# "BCDD::Context::Expectations"
|
2273
2443
|
# ]
|
2274
2444
|
# }
|
2275
2445
|
# }
|
@@ -2286,21 +2456,21 @@ BCDD::Result.config.feature.options
|
|
2286
2456
|
# :enabled=>true,
|
2287
2457
|
# :affects=>[
|
2288
2458
|
# "BCDD::Result::Expectations,
|
2289
|
-
# "BCDD::
|
2459
|
+
# "BCDD::Context::Expectations"
|
2290
2460
|
# ]
|
2291
2461
|
# },
|
2292
|
-
#
|
2462
|
+
# event_logs=>{
|
2293
2463
|
# :enabled=>true,
|
2294
2464
|
# :affects=>[
|
2295
2465
|
# "BCDD::Result",
|
2296
|
-
# "BCDD::
|
2466
|
+
# "BCDD::Context"
|
2297
2467
|
# ]
|
2298
2468
|
# },
|
2299
2469
|
# :and_then!=>{
|
2300
2470
|
# :enabled=>false,
|
2301
2471
|
# :affects=>[
|
2302
2472
|
# "BCDD::Result",
|
2303
|
-
# "BCDD::
|
2473
|
+
# "BCDD::Context"
|
2304
2474
|
# ]
|
2305
2475
|
# },
|
2306
2476
|
# }
|
@@ -2343,7 +2513,7 @@ class PlaceOrder < Micro::Case
|
|
2343
2513
|
end
|
2344
2514
|
```
|
2345
2515
|
|
2346
|
-
To facilitate migration for users accustomed to the above approaches, `bcdd-result` includes the `BCDD::Result#and_then!`/`BCDD::
|
2516
|
+
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
2517
|
|
2348
2518
|
```ruby
|
2349
2519
|
BCDD::Result.configure do |config|
|
@@ -2351,7 +2521,7 @@ BCDD::Result.configure do |config|
|
|
2351
2521
|
end
|
2352
2522
|
|
2353
2523
|
class PlaceOrder
|
2354
|
-
include BCDD::
|
2524
|
+
include BCDD::Context.mixin
|
2355
2525
|
|
2356
2526
|
def call(**input)
|
2357
2527
|
Given(input)
|
@@ -2383,11 +2553,11 @@ class PlaceOrder
|
|
2383
2553
|
end
|
2384
2554
|
```
|
2385
2555
|
|
2386
|
-
**In BCDD::
|
2556
|
+
**In BCDD::Context**
|
2387
2557
|
|
2388
2558
|
```ruby
|
2389
2559
|
class PlaceOrder
|
2390
|
-
include BCDD::
|
2560
|
+
include BCDD::Context.mixin
|
2391
2561
|
|
2392
2562
|
def call(logger:, **input)
|
2393
2563
|
Given(input)
|
@@ -2441,7 +2611,7 @@ Attention: to ensure the correct behavior, do not mix `#and_then` and `#and_then
|
|
2441
2611
|
|
2442
2612
|
#### Analysis: Why is `#and_then` the antidote/standard?
|
2443
2613
|
|
2444
|
-
The `BCDD::Result#and_then`/`BCDD::
|
2614
|
+
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
2615
|
|
2446
2616
|
- **Clarity:** The input/output relationship between the steps is apparent and highly understandable.
|
2447
2617
|
|
@@ -2451,7 +2621,7 @@ See this example to understand what your code should look like:
|
|
2451
2621
|
|
2452
2622
|
```ruby
|
2453
2623
|
class PlaceOrder
|
2454
|
-
include BCDD::
|
2624
|
+
include BCDD::Context.mixin(config: { addon: { continue: true } })
|
2455
2625
|
|
2456
2626
|
def call(**input)
|
2457
2627
|
Given(input)
|