solid-result 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +98 -0
  3. data/.rubocop_todo.yml +12 -0
  4. data/CHANGELOG.md +600 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +2691 -0
  8. data/Rakefile +28 -0
  9. data/Steepfile +31 -0
  10. data/examples/multiple_listeners/Rakefile +55 -0
  11. data/examples/multiple_listeners/app/models/account/member.rb +10 -0
  12. data/examples/multiple_listeners/app/models/account/owner_creation.rb +62 -0
  13. data/examples/multiple_listeners/app/models/account.rb +11 -0
  14. data/examples/multiple_listeners/app/models/user/creation.rb +67 -0
  15. data/examples/multiple_listeners/app/models/user/token/creation.rb +51 -0
  16. data/examples/multiple_listeners/app/models/user/token.rb +7 -0
  17. data/examples/multiple_listeners/app/models/user.rb +15 -0
  18. data/examples/multiple_listeners/config/boot.rb +16 -0
  19. data/examples/multiple_listeners/config/initializers/solid_result.rb +9 -0
  20. data/examples/multiple_listeners/config.rb +27 -0
  21. data/examples/multiple_listeners/db/setup.rb +60 -0
  22. data/examples/multiple_listeners/lib/event_logs_listener/stdout.rb +60 -0
  23. data/examples/multiple_listeners/lib/runtime_breaker.rb +11 -0
  24. data/examples/multiple_listeners/lib/solid/result/event_logs_record.rb +27 -0
  25. data/examples/multiple_listeners/lib/solid/result/rollback_on_failure.rb +15 -0
  26. data/examples/service_objects/Rakefile +36 -0
  27. data/examples/service_objects/app/models/account/member.rb +10 -0
  28. data/examples/service_objects/app/models/account.rb +11 -0
  29. data/examples/service_objects/app/models/user/token.rb +7 -0
  30. data/examples/service_objects/app/models/user.rb +15 -0
  31. data/examples/service_objects/app/services/account/owner_creation.rb +47 -0
  32. data/examples/service_objects/app/services/application_service.rb +79 -0
  33. data/examples/service_objects/app/services/user/creation.rb +56 -0
  34. data/examples/service_objects/app/services/user/token/creation.rb +37 -0
  35. data/examples/service_objects/config/boot.rb +17 -0
  36. data/examples/service_objects/config/initializers/solid_result.rb +9 -0
  37. data/examples/service_objects/config.rb +20 -0
  38. data/examples/service_objects/db/setup.rb +49 -0
  39. data/examples/single_listener/Rakefile +92 -0
  40. data/examples/single_listener/app/models/account/member.rb +10 -0
  41. data/examples/single_listener/app/models/account/owner_creation.rb +62 -0
  42. data/examples/single_listener/app/models/account.rb +11 -0
  43. data/examples/single_listener/app/models/user/creation.rb +67 -0
  44. data/examples/single_listener/app/models/user/token/creation.rb +51 -0
  45. data/examples/single_listener/app/models/user/token.rb +7 -0
  46. data/examples/single_listener/app/models/user.rb +15 -0
  47. data/examples/single_listener/config/boot.rb +16 -0
  48. data/examples/single_listener/config/initializers/solid_result.rb +9 -0
  49. data/examples/single_listener/config.rb +23 -0
  50. data/examples/single_listener/db/setup.rb +49 -0
  51. data/examples/single_listener/lib/runtime_breaker.rb +11 -0
  52. data/examples/single_listener/lib/single_event_logs_listener.rb +117 -0
  53. data/examples/single_listener/lib/solid/result/rollback_on_failure.rb +15 -0
  54. data/lib/solid/failure.rb +23 -0
  55. data/lib/solid/output/callable_and_then.rb +40 -0
  56. data/lib/solid/output/expectations/mixin.rb +31 -0
  57. data/lib/solid/output/expectations.rb +25 -0
  58. data/lib/solid/output/failure.rb +9 -0
  59. data/lib/solid/output/mixin.rb +57 -0
  60. data/lib/solid/output/success.rb +37 -0
  61. data/lib/solid/output.rb +115 -0
  62. data/lib/solid/result/_self.rb +198 -0
  63. data/lib/solid/result/callable_and_then/caller.rb +49 -0
  64. data/lib/solid/result/callable_and_then/config.rb +15 -0
  65. data/lib/solid/result/callable_and_then/error.rb +11 -0
  66. data/lib/solid/result/callable_and_then.rb +9 -0
  67. data/lib/solid/result/config/options.rb +27 -0
  68. data/lib/solid/result/config/switcher.rb +82 -0
  69. data/lib/solid/result/config/switchers/addons.rb +25 -0
  70. data/lib/solid/result/config/switchers/constant_aliases.rb +33 -0
  71. data/lib/solid/result/config/switchers/features.rb +32 -0
  72. data/lib/solid/result/config/switchers/pattern_matching.rb +20 -0
  73. data/lib/solid/result/config.rb +64 -0
  74. data/lib/solid/result/contract/disabled.rb +25 -0
  75. data/lib/solid/result/contract/error.rb +17 -0
  76. data/lib/solid/result/contract/evaluator.rb +45 -0
  77. data/lib/solid/result/contract/for_types.rb +29 -0
  78. data/lib/solid/result/contract/for_types_and_values.rb +46 -0
  79. data/lib/solid/result/contract/interface.rb +21 -0
  80. data/lib/solid/result/contract/type_checker.rb +37 -0
  81. data/lib/solid/result/contract.rb +33 -0
  82. data/lib/solid/result/data.rb +33 -0
  83. data/lib/solid/result/error.rb +59 -0
  84. data/lib/solid/result/event_logs/config.rb +28 -0
  85. data/lib/solid/result/event_logs/listener.rb +51 -0
  86. data/lib/solid/result/event_logs/listeners.rb +87 -0
  87. data/lib/solid/result/event_logs/tracking/disabled.rb +15 -0
  88. data/lib/solid/result/event_logs/tracking/enabled.rb +161 -0
  89. data/lib/solid/result/event_logs/tracking.rb +26 -0
  90. data/lib/solid/result/event_logs/tree.rb +141 -0
  91. data/lib/solid/result/event_logs.rb +27 -0
  92. data/lib/solid/result/expectations/mixin.rb +58 -0
  93. data/lib/solid/result/expectations.rb +75 -0
  94. data/lib/solid/result/failure.rb +11 -0
  95. data/lib/solid/result/handler/allowed_types.rb +45 -0
  96. data/lib/solid/result/handler.rb +57 -0
  97. data/lib/solid/result/ignored_types.rb +14 -0
  98. data/lib/solid/result/mixin.rb +72 -0
  99. data/lib/solid/result/success.rb +11 -0
  100. data/lib/solid/result/version.rb +7 -0
  101. data/lib/solid/result.rb +27 -0
  102. data/lib/solid/success.rb +23 -0
  103. data/lib/solid-result.rb +3 -0
  104. data/sig/solid/failure.rbs +13 -0
  105. data/sig/solid/output.rbs +175 -0
  106. data/sig/solid/result/callable_and_then.rbs +60 -0
  107. data/sig/solid/result/config.rbs +102 -0
  108. data/sig/solid/result/contract.rbs +120 -0
  109. data/sig/solid/result/data.rbs +16 -0
  110. data/sig/solid/result/error.rbs +34 -0
  111. data/sig/solid/result/event_logs.rbs +189 -0
  112. data/sig/solid/result/expectations.rbs +71 -0
  113. data/sig/solid/result/handler.rbs +47 -0
  114. data/sig/solid/result/ignored_types.rbs +9 -0
  115. data/sig/solid/result/mixin.rbs +45 -0
  116. data/sig/solid/result/version.rbs +5 -0
  117. data/sig/solid/result.rbs +85 -0
  118. data/sig/solid/success.rbs +13 -0
  119. metadata +167 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,600 @@
1
+ - [\[Unreleased\]](#unreleased)
2
+ - [2.0.0 - 2024-04-13](#200---2024-04-13)
3
+ - [Changed](#changed)
4
+ - [1.1.0 - 2024-03-25](#110---2024-03-25)
5
+ - [Added](#added)
6
+ - [1.0.0 - 2024-03-16](#100---2024-03-16)
7
+ - [Added](#added-1)
8
+ - [Changed](#changed-1)
9
+ - [\[0.13.0\] - 2024-02-01](#0130---2024-02-01)
10
+ - [Added](#added-2)
11
+ - [Changed](#changed-2)
12
+ - [\[0.12.0\] - 2024-01-07](#0120---2024-01-07)
13
+ - [Added](#added-3)
14
+ - [Changed](#changed-3)
15
+ - [\[0.11.0\] - 2024-01-02](#0110---2024-01-02)
16
+ - [Added](#added-4)
17
+ - [Changed](#changed-4)
18
+ - [\[0.10.0\] - 2023-12-31](#0100---2023-12-31)
19
+ - [Added](#added-5)
20
+ - [\[0.9.1\] - 2023-12-12](#091---2023-12-12)
21
+ - [Changed](#changed-5)
22
+ - [Fixed](#fixed)
23
+ - [\[0.9.0\] - 2023-12-12](#090---2023-12-12)
24
+ - [Added](#added-6)
25
+ - [Changed](#changed-6)
26
+ - [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
27
+ - [Added](#added-7)
28
+ - [Changed](#changed-7)
29
+ - [Removed](#removed)
30
+ - [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
31
+ - [Added](#added-8)
32
+ - [Changed](#changed-8)
33
+ - [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
34
+ - [Added](#added-9)
35
+ - [Changed](#changed-9)
36
+ - [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
37
+ - [Added](#added-10)
38
+ - [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
39
+ - [Added](#added-11)
40
+ - [Changed](#changed-10)
41
+ - [Removed](#removed-1)
42
+ - [\[0.3.0\] - 2023-09-26](#030---2023-09-26)
43
+ - [Added](#added-12)
44
+ - [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
45
+ - [Added](#added-13)
46
+ - [Removed](#removed-2)
47
+ - [\[0.1.0\] - 2023-09-25](#010---2023-09-25)
48
+ - [Added](#added-14)
49
+
50
+ ## [Unreleased]
51
+
52
+ ## 2.0.0 - 2024-04-13
53
+
54
+ ### Changed
55
+
56
+ - **(BREAKING)** Rebrand the gem from `bcdd-result` to `Solid::Result`.
57
+ - `Solid::Result` replaces `BCDD::Result`.
58
+ - `Solid::Output` replaces `BCDD::Context`.
59
+
60
+ ## 1.1.0 - 2024-03-25
61
+
62
+ ### Added
63
+
64
+ - Add some Hash's methods to `BCDD::Context`. They are:
65
+ - `#slice` to extract only the desired keys.
66
+ - `#[]`, `#dig`, `#fetch` to access the values.
67
+ - `#values_at` and `#fetch_values` to get the values of the desired keys.
68
+
69
+ ## 1.0.0 - 2024-03-16
70
+
71
+ ### Added
72
+
73
+ - Add the `BCDD::Success` and `BCDD::Failure` modules. They are key to checking whether a result is a success or a failure independently of whether it is a `BCDD::Result` or a `BCDD::Context`.
74
+ - Add `BCDD::Result#type?` to check if the given type is the result type.
75
+ - Add `BCDD::Result#is?` as an alias for `BCDD::Result#type?`.
76
+ - Add `BCDD::Result#method_missing` to allow the type checking through method calls. For example, `result.ok?` will check if the result type is `:ok`.
77
+
78
+ > Note: All the methods above are available for the `BCDD::Context` as well.
79
+
80
+ ### Changed
81
+
82
+ - **(BREAKING)** Replace transitions with event_logs concept.
83
+ - The `BCDD::Result::Transitions` module was renamed to `BCDD::Result::EventLogs`
84
+ - The `BCDD::Result.transitions` to `BCDD::Result.event_logs`.
85
+
86
+ - **(BREAKING)** Change `BCDD::Result#deconstruct_keys` to return a hash with the keys `:type` and `:value` when one of these keys is present. Otherwise, it will return the value itself.
87
+
88
+ - **(BREAKING)** Replace trasitions metadata `:ids_tree`, and `:ids_matrix` with `:ids` property. This property is a hash with the following keys:
89
+ - `:tree`, a graph/tree representation of the transitions ids.
90
+ - `:level_parent`, a hash with the level (depth) of each transition and its parent id.
91
+ - `:matrix`, a matrix representation of the transitions ids. It is a simplification of the `:tree` property.
92
+
93
+ - Transform `BCDD::Result::Context` into `BCDD::Context`. But a constant alias was added to keep the old name. You can use `BCDD::Result::Context` or `BCDD::Context` to access the same class.
94
+
95
+ ## [0.13.0] - 2024-02-01
96
+
97
+ ### Added
98
+
99
+ - `BCDD::Result::Context#and_expose` - Raise error when trying to expose an invalid key.
100
+
101
+ - `BCDD::Result.configuration` - Accept freeze option (default: `true`). When true, the configuration will be frozen after the block execution.
102
+
103
+ - `BCDD::Result.config.transitions` - Add transitions feature configuration.
104
+ - `config.transitions.listener =` - Set a listener to be called during the result transitions tracking. It must be a class that includes `BCDD::Result::Transitions::Listener`.
105
+ - `config.transitions.trace_id =` - Set a lambda (must have arity 0) to be called to get a trace id. Use to correlate different or the same operation (executed multiple times).
106
+
107
+ - Add transitions metadata property `:ids_matrix`. It is a simplification of the `:ids_tree` property. The matrix rows are the direct transitions from the root transition block, and the columns are the transitions nested from the direct transitions.
108
+ ```ruby
109
+ # ids_matrix # {
110
+ 0 | 1 | 2 | 3 | 4 # 0 => [0, 0],
111
+ - | - | - | - | - # 1 => [1, 1],
112
+ 0 | | | | # 2 => [1, 2],
113
+ 1 | 1 | 2 | | # 3 => [2, 1],
114
+ 2 | 3 | | | # 4 => [3, 1],
115
+ 3 | 4 | 5 | 6 | 7 # 5 => [3, 2],
116
+ 4 | 8 | | | # 6 => [3, 3],
117
+ # 7 => [3, 4],
118
+ # 8 => [4, 1]
119
+ # }
120
+ ```
121
+
122
+ - Add `BCDD::Result::Transitions::Listeners[]` - It creates a listener of listeners, which will be called in the order they were added.
123
+
124
+ ### Changed
125
+
126
+ - **(BREAKING)** Rename `Given()` type from `:given` to `:_given_`.
127
+
128
+ - **(BREAKING)** Rename `Continue()` type from `:continued` to `:_continue_`.
129
+
130
+ - **(BREAKING)** Move transition `:source` from `:and_then` to `:result` property.
131
+
132
+ - **(BREAKING)** Rename transitions metadata property `:tree_map` to `:ids_tree`.
133
+ ```ruby
134
+ # ids_tree #
135
+ 0 # [0, [
136
+ |- 1 # [1, [[2, []]]],
137
+ | |- 2 # [3, []],
138
+ |- 3 # [4, [
139
+ |- 4 # [5, []],
140
+ | |- 5 # [6, [[7, []]]]
141
+ | |- 6 # ]],
142
+ | |- 7 # [8, []]
143
+ |- 8 # ]]
144
+ ```
145
+
146
+ ## [0.12.0] - 2024-01-07
147
+
148
+ ### Added
149
+
150
+ - Add `BCDD::Result#and_then!` and `BCDD::Result::Context#and_then!` to execute a callable object (any object that responds to `#call`) to produce a result. The main difference between the `#and_then` and `#and_then!` is that the latter does not check the result source.
151
+ - **Attention:** to ensure the correct behavior, do not mix `#and_then` and `#and_then!` in the same result chain.
152
+ - This feature is turned off by default. You can enable it through the `BCDD::Result.config.feature.enable!(:and_then!)`.
153
+ - The method called by default (`:call`) can be changed through `BCDD::Result.config.and_then!.default_method_name_to_call=`.
154
+
155
+ ### Changed
156
+
157
+ - **(BREAKING)** Renames the subject concept/term to `source`. When a mixin is included/extended, it defines the `Success()` and `Failure()` methods. Since the results are generated in a context (instance or singleton where the mixin was used), they will have a defined source (instance or singleton itself).
158
+ > Definition of source
159
+ >
160
+ > From dictionary:
161
+ > * a place, person, or thing from which something comes or can be obtained.
162
+
163
+ ## [0.11.0] - 2024-01-02
164
+
165
+ ### Added
166
+
167
+ - Add the `Given()` addon to produce a `Success(:given, value)` result. As the `Continue()` addon, it is ignored by the expectations. Use it to add a value to the result chain and invoke the next step (through `and_then`).
168
+
169
+ ### Changed
170
+
171
+ - **(BREAKING)** Rename halted concept to terminal. Failures are terminal by default, but you can make a success terminal by enabling the `:continue` addon.
172
+ > Definition of terminal
173
+ >
174
+ > From dictionary:
175
+ > * of, forming, or situated at the end or extremity of something.
176
+ > * the end of a railroad or other transport route, or a station at such a point.
177
+ >
178
+ > From Wikipedia:
179
+ > * A "terminus" or "terminal" is a station at the end of a railway line.
180
+
181
+ - **(BREAKING)** Rename `BCDD::Result::Context::Success#and_expose` halted keyword argument to `terminal`.
182
+
183
+ - **(BREAKING)** Rename `BCDD::Result#halted?` to `BCDD::Result#terminal?`.
184
+
185
+ ## [0.10.0] - 2023-12-31
186
+
187
+ ### Added
188
+
189
+ - Add `BCDD::Result.transitions(&block)` to track all transitions in the same or between different operations. When there is a nesting of transition blocks, this mechanism will be able to correlate parent and child blocks and present the duration of all operations in milliseconds.
190
+
191
+ - Add `BCDD::Result.config.feature.disable!(:transitions)` and `BCDD::Result.config.feature.enable!(:transitions)` to turn on/off the `BCDD::Result.transitions` feature.
192
+
193
+ ## [0.9.1] - 2023-12-12
194
+
195
+ ### Changed
196
+
197
+ - **(BREAKING)** Make `BCDD::Result::Context::Success#and_expose()` to produce a terminal success by default. You can turn this off by passing `halted: false`.
198
+
199
+ ### Fixed
200
+
201
+ - Make `BCDD::Result::Context#and_then(&block)` accumulate the result value.
202
+
203
+ ## [0.9.0] - 2023-12-12
204
+
205
+ ### Added
206
+
207
+ - Add new `BCDD::Result.config.constant_alias` options. `Context` and `BCDD::Context` are now available as aliases for `BCDD::Result::Context`.
208
+ ```ruby
209
+ BCDD::Result.config.constant_alias.enable!('Context')
210
+
211
+ BCDD::Result.config.constant_alias.enable!('BCDD::Context')
212
+ ```
213
+
214
+ - Add `BCDD::Result#halted?` to check if the result is halted. Failure results are halted by default, but you can halt a successful result by enabling the `:continue` addon.
215
+
216
+ ### Changed
217
+
218
+ - **(BREAKING)** Change the `:continue` addon to halt the step chain on the first `Success()` result. 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 halted. (Implementation of the following proposal: https://github.com/B-CDD/result/issues/14)
219
+
220
+ - **(BREAKING)** Rename `BCDD::Result::Data#name` to `BCDD::Result::Data#kind`. The new word is more appropriate as it represents a result's kind (success or failure).
221
+
222
+ ## [0.8.0] - 2023-12-11
223
+
224
+ ### Added
225
+
226
+ - Add `BCDD::Result.config`
227
+ - **Feature**
228
+ ```ruby
229
+ BCDD::Result.config.feature.options
230
+ BCDD::Result.config.feature.enabled?(:expectations)
231
+ BCDD::Result.config.feature.enable!(:expectations)
232
+ BCDD::Result.config.feature.disable!(:expectations)
233
+ ```
234
+ - **Default Add-ons**
235
+ ```ruby
236
+ BCDD::Result.config.addon.options
237
+ BCDD::Result.config.addon.enabled?(:continue)
238
+ BCDD::Result.config.addon.enable!(:continue)
239
+ BCDD::Result.config.addon.disable!(:continue)
240
+ ```
241
+ - **Pattern matching**
242
+ ```ruby
243
+ BCDD::Result.config.pattern_matching.options
244
+ BCDD::Result.config.pattern_matching.enabled?(:nil_as_valid_value_checking)
245
+ BCDD::Result.config.pattern_matching.enable!(:nil_as_valid_value_checking)
246
+ BCDD::Result.config.pattern_matching.disable!(:nil_as_valid_value_checking)
247
+ ```
248
+ - **Constant Aliases**
249
+ ```ruby
250
+ BCDD::Result.config.constant_alias.options
251
+ BCDD::Result.config.constant_alias.enabled?('Result')
252
+ BCDD::Result.config.constant_alias.enable!('Result')
253
+ BCDD::Result.config.constant_alias.disable!('Result')
254
+ ```
255
+
256
+ - Add `BCDD::Result::configuration`. It freezes the configuration, disallowing methods that promote changes but allowing the query ones. You can use this feature to ensure integrity in your configuration.
257
+ ```ruby
258
+ BCDD::Result.configuration do |config|
259
+ config.addon.enable!(:continue)
260
+
261
+ config.constant_alias.enable!('Result')
262
+
263
+ config.pattern_matching.disable!(:nil_as_valid_value_checking)
264
+
265
+ config.feature.disable!(:expectations) if ::Rails.env.production?
266
+ end
267
+
268
+ BCDD::Result.config.addon.enabled?(:continue) # true
269
+ BCDD::Result.config.constant_alias.enabled?('Result') # true
270
+
271
+ BCDD::Result.config.addon.disable!(:continue) # raises FrozenError
272
+ BCDD::Result.config.constant_alias.disable!('Result') # raises FrozenError
273
+ ```
274
+
275
+ - Allow the pattern matching feature to be turned on/off through the `BCDD::Result::Expectations.mixin`. Now, it can be used without enabling it for the whole project.
276
+ ```ruby
277
+ extend BCDD::Result::Expectations.mixin(
278
+ config: {
279
+ addon: { continue: false },
280
+ pattern_matching: { nil_as_valid_value_checking: true },
281
+ },
282
+ success: {
283
+ numbers: ->(value) { value => [Numeric, Numeric] },
284
+ division_completed: Numeric
285
+ },
286
+ failure: {
287
+ invalid_arg: String,
288
+ division_by_zero: String
289
+ }
290
+ )
291
+ ```
292
+
293
+ ### Changed
294
+
295
+ - **(BREAKING)** Replace `BCDD::Result::Contract.nil_as_valid_value_checking!` with `BCDD::Result::Config.pattern_matching.enable!(:nil_as_valid_value_checking)`.
296
+
297
+ - **(BREAKING)** Replace `BCDD::Result::Contract.nil_as_valid_value_checking?` with `BCDD::Result::Config.pattern_matching.enabled?(:nil_as_valid_value_checking)`.
298
+
299
+ - **(BREAKING)** Replace `mixin(with:)` with `mixin(config:)` keyword argument.
300
+
301
+ - **(BREAKING)** Change the addons definition.
302
+ - **From**
303
+ ```ruby
304
+ BCDD::Result.mixin(with: :Continue)
305
+ BCDD::Result.mixin(with: [:Continue])
306
+ ```
307
+ - **To**
308
+ ```ruby
309
+ BCDD::Result.mixin(config: { addon: { continue: true } })
310
+ ```
311
+ - These examples are valid to all kinds of mixins (`BCDD::Result.mixin`, `BCDD::Result::Context.mixin`, `BCDD::Result::Expectations.mixin`, `BCDD::Result::Context::Expectations.mixin`)
312
+
313
+ ### Removed
314
+
315
+ - **(BREAKING)** Remove the `lib/result` file. Now you can define `Result` as an alias for `BCDD::Result` using `BCDD::Result::Config.constant_alias.enable!('Result')`.
316
+
317
+ ## [0.7.0] - 2023-10-27
318
+
319
+ ### Added
320
+
321
+ - Add `BCDD::Result::Context`. It 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.<br/><br/>
322
+ 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::Result::Context` has the `#and_expose` method to expose only the desired keys from the accumulated result.
323
+
324
+ - Add `BCDD::Result::Context::Expectations.new` and `BCDD::Result::Context::Expectations.mixin`. Both are similar to `BCDD::Result::Expectations.new` and `BCDD::Result::Expectations.mixin`, but they are for `BCDD::Result::Context` instead of `BCDD::Result`.
325
+ - The `BCDD::Result::Context.mixin` and `BCDD::Result::Context::Expectations.mixin` support the `with: :Continue` option.
326
+
327
+ - Enhance Pattern Matching support. When a `NoMatchingPatternError` occurs inside a value checking, the `BCDD::Result::Contract::Error::UnexpectedValue` message will include the value and the expected patterns.
328
+
329
+ - Add `BCDD::Result::Success::Methods` to be share common methods between `BCDD::Result::Success` and `BCDD::Result::Context::Success`.
330
+
331
+ - Add `BCDD::Result::Failure::Methods` to be share common methods between `BCDD::Failure::Success` and `BCDD::Result::Context::Failure`.
332
+
333
+ - Make all mixin generators produce a named module. The module name will be added to the target class/module (who included/extended a `BCDD::Result`/`BCDD::Result::Context` mixin module).
334
+
335
+ - Add `BCDD::Result::Contract.nil_as_valid_value_checking!`. Please use this method when using the one-line pattern-matching operators on the result's value expectations.
336
+
337
+ ### Changed
338
+
339
+ - **(BREAKING)** Rename `BCDD::Result::WrongResultSubject` to `BCDD::Result::Error::InvalidResultSubject`.
340
+ - **(BREAKING)** Rename `BCDD::Result::WrongSubjectMethodArity` to `BCDD::Result::Error::InvalidSubjectMethodArity`.
341
+ - **(BREAKING)** Rename the constant produced by `BCDD::Result::Expectations.mixins` from `Expected` to `Result`.
342
+ - Extract the major part of the `BCDD::Result::Expectations` components/features to `BCDD::Result::Contract`.
343
+ - **(BREAKING)** `BCDD::Result::Expectations::Error` became `BCDD::Result::Contract::Error`. So, `BCDD::Result::Expectations::Error::UnexpectedType` and `BCDD::Result::Expectations::Error::UnexpectedValue` are now `BCDD::Result::Contract::Error::UnexpectedType` and `BCDD::Result::Contract::Error::UnexpectedValue`.
344
+
345
+ ## [0.6.0] - 2023-10-11
346
+
347
+ ### Added
348
+
349
+ - Add `BCDD::Result.mixin` to be included or extended in any object. It will add `Success()` and `Failure()` to the target object (the object who receives the include/extend).
350
+
351
+ - Add `BCDD::Result.mixin(with: :Continue)`. This addon will add a `Continue(value)` method to the target object to produce a `Success(:continued, value)` result.
352
+
353
+ - Add `BCDD::Result::Expectations.mixin(with: :Continue)`, it is similar to `BCDD::Result.mixin(with: :Continue)`, the key difference is that the `Continue(value)` will be ignored by the expectations. This is extremely useful when you want to use `Continue(value)` to chain operations, but you don't want to declare N success types in the expectations.
354
+
355
+ - Increase the arity of `BCDD::Result#and_then`. Now, it can receive a second argument (a value to be injected and shared with the subject's method).
356
+
357
+ - Increase the arity (maximum of 2) for the methods called through `BCDD::Result#and_then`. The second argument is the value injected by `BCDD::Result#and_then`.
358
+
359
+ ### Changed
360
+
361
+ - **(BREAKING)** Make `BCDD::Result::Mixin` be a private constant. The `BCDD::Result.mixin` method is the new way to use it.
362
+
363
+ ## [0.5.0] - 2023-10-09
364
+
365
+ ### Added
366
+
367
+ - Add `BCDD::Result::Expectations` to define contracts for your results. There are two ways to use it: the standalone (`BCDD::Result::Expectations.new`) and the mixin (`BCDD::Result::Expectations.mixin`) mode.
368
+
369
+ The main difference is that the mixin mode will use the target object (who receives the include/extend) as the result's subject (like the `BCDD::Result::Mixin` does), while the standalone mode won't.
370
+
371
+ **Standalone mode:**
372
+
373
+ ```ruby
374
+ module Divide
375
+ Expected = BCDD::Result::Expectations.new(
376
+ success: {
377
+ numbers: ->(value) { value.is_a?(Array) && value.size == 2 && value.all?(Numeric) },
378
+ division_completed: Numeric
379
+ },
380
+ failure: {
381
+ invalid_arg: String,
382
+ division_by_zero: String
383
+ }
384
+ )
385
+
386
+ def self.call(arg1, arg2)
387
+ arg1.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg1 must be numeric')
388
+ arg2.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg2 must be numeric')
389
+
390
+ arg2.zero? and return Expected::Failure(:division_by_zero, 'arg2 must not be zero')
391
+
392
+ Expected::Success(:division_completed, arg1 / arg2)
393
+ end
394
+ end
395
+ ```
396
+
397
+ **Mixin mode:**
398
+
399
+ ```ruby
400
+ class Divide
401
+ include BCDD::Result::Expectations.mixin(
402
+ success: {
403
+ numbers: ->(value) { value.is_a?(Array) && value.size == 2 && value.all?(Numeric) },
404
+ division_completed: Numeric
405
+ },
406
+ failure: {
407
+ invalid_arg: String,
408
+ division_by_zero: String
409
+ }
410
+ )
411
+
412
+ def call(arg1, arg2)
413
+ validate_numbers(arg1, arg2)
414
+ .and_then(:validate_non_zero)
415
+ .and_then(:divide)
416
+ end
417
+
418
+ private
419
+
420
+ def validate_numbers(arg1, arg2)
421
+ arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
422
+ arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
423
+
424
+ Success(:numbers, [arg1, arg2])
425
+ end
426
+
427
+ def validate_non_zero(numbers)
428
+ return Success(:numbers, numbers) unless numbers.last.zero?
429
+
430
+ Failure(:division_by_zero, 'arg2 must not be zero')
431
+ end
432
+
433
+ def divide((number1, number2))
434
+ Success(:division_completed, number1 / number2)
435
+ end
436
+ end
437
+ ```
438
+
439
+ ## [0.4.0] - 2023-09-28
440
+
441
+ ### Added
442
+
443
+ - Add `require 'result'` to define `Result` as an alias for `BCDD::Result`.
444
+
445
+ - Add support to pattern matching (Ruby 2.7+).
446
+
447
+ - Add `BCDD::Result#on_unknown` to execute a block if no other hook (`#on`, `#on_type`, `#on_failure`, `#on_success`) has been executed. Attention: always use it as the last hook.
448
+
449
+ - Add `BCDD::Result::Handler#unknown` to execute a block if no other handler (`#[]`, `#type`, `#failure`, `#success`) has been executed. Attention: always use it as the last handler.
450
+
451
+ ### Changed
452
+
453
+ - **(BREAKING)** Rename `BCDD::Resultable` to `BCDD::Result::Mixin`.
454
+
455
+ - **(BREAKING)** Change `BCDD::Result#data` to return a `BCDD::Result::Data` instead of the result value. This object exposes the result attributes (name, type, value) directly and as a hash (`to_h`/`to_hash`) and array (`to_a`/`to_ary`).
456
+
457
+ ### Removed
458
+
459
+ - **(BREAKING)** Remove `BCDD::Result#data_or`.
460
+
461
+ ## [0.3.0] - 2023-09-26
462
+
463
+ ### Added
464
+
465
+ - Add `BCDD::Result#handle`. This method allows defining blocks for each hook (type, failure, success), but instead of returning the result itself, it will return the output of the first match/block execution.
466
+
467
+ ## [0.2.0] - 2023-09-26
468
+
469
+ ### Added
470
+
471
+ - Add `BCDD::Resultable`. This module can add `Success()` and `Failure()` in any object. The target object will be the subject of the result object produced by these methods.
472
+
473
+ **Classes (instance methods)**
474
+
475
+ ```ruby
476
+ class Divide
477
+ include BCDD::Resultable
478
+
479
+ attr_reader :arg1, :arg2
480
+
481
+ def initialize(arg1, arg2)
482
+ @arg1 = arg1
483
+ @arg2 = arg2
484
+ end
485
+
486
+ def call
487
+ validate_numbers
488
+ .and_then(:validate_non_zero)
489
+ .and_then(:divide)
490
+ end
491
+
492
+ private
493
+
494
+ def validate_numbers
495
+ arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
496
+ arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
497
+
498
+ Success(:ok, [arg1, arg2])
499
+ end
500
+
501
+ def validate_non_zero(numbers)
502
+ return Success(:ok, numbers) unless numbers.last.zero?
503
+
504
+ Failure(:division_by_zero, 'arg2 must not be zero')
505
+ end
506
+
507
+ def divide((number1, number2))
508
+ Success(:division_completed, number1 / number2)
509
+ end
510
+ end
511
+ ```
512
+
513
+ **Module (singleton methods)**
514
+
515
+ ```ruby
516
+ module Divide
517
+ extend self, BCDD::Resultable
518
+
519
+ def call(arg1, arg2)
520
+ validate_numbers(arg1, arg2)
521
+ .and_then(:validate_non_zero)
522
+ .and_then(:divide)
523
+ end
524
+
525
+ private
526
+
527
+ def validate_numbers(arg1, arg2)
528
+ arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
529
+ arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
530
+
531
+ Success(:ok, [arg1, arg2])
532
+ end
533
+
534
+ def validate_non_zero(numbers)
535
+ return Success(:ok, numbers) unless numbers.last.zero?
536
+
537
+ Failure(:division_by_zero, 'arg2 must not be zero')
538
+ end
539
+
540
+ def divide((number1, number2))
541
+ Success(:division_completed, number1 / number2)
542
+ end
543
+ end
544
+ ```
545
+
546
+ - Make the `BCDD::Result#initialize` enabled to receive a subject.
547
+
548
+ - Make the `BCDD::Result#and_then` method receive a method name (symbol) and perform it on the result subject (added by `BCDD::Resultable`). The called method must return a result; otherwise, an error (`BCDD::Result::Error::UnexpectedOutcome`) will be raised.
549
+
550
+ - Add `BCDD::Result::Error::UnexpectedOutcome` to represent an unexpected outcome.
551
+
552
+ - Add `BCDD::Result::Error::WrongResultSubject` to represent a wrong result subject. When using `BCDD::Resultable`, the result subject must be the same as the target object.
553
+
554
+ - Add `BCDD::Result::Error::WrongSubjectMethodArity` to represent a wrong subject method arity. Valid arities are 0 and 1.
555
+
556
+ ### Removed
557
+
558
+ - **(BREAKING)** Remove `BCDD::Result::Error::UnexpectedBlockOutcome`. It was replaced by `BCDD::Result::Error::UnexpectedOutcome`.
559
+
560
+ ## [0.1.0] - 2023-09-25
561
+
562
+ ### Added
563
+
564
+ - Add `BCDD::Result` to represent a result.
565
+
566
+ - Add `BCDD::Result#type` to get the result type. The type must be a symbol.
567
+
568
+ - Add `BCDD::Result#value` to get the result value. The value can be anything.
569
+
570
+ - Add `BCDD::Result#success?` to check if the result is a success. You can also check the result type by passing an argument to it. For example, `result.success?(:ok)` will check if the result is a success and if the type is `:ok`.
571
+
572
+ - Add `BCDD::Result#failure?` to check if the result is a failure. You can also check the result type by passing an argument to it. For example, `result.failure?(:error)` will check if the result is a failure and if the type is `:error`.
573
+
574
+ - Add `BCDD::Result#value_or` to get the value of a successful result or a default value (from the block) if it is a failure.
575
+
576
+ - Add `BCDD::Result#==` to compare two results.
577
+
578
+ - Add `BCDD::Result#eql?` to compare two results.
579
+
580
+ - Add `BCDD::Result#hash` to get the hash of a result.
581
+
582
+ - Add `BCDD::Result#inspect` to get the string representation of a result.
583
+
584
+ - Add `BCDD::Result#on` to execute a block depending on the result type (independently of the result being a success or a failure). The block will receive the result value as an argument, and the result itself will be returned after (or not) the block execution. The method can be called multiple times and with one or more arguments. For example, `result.on(:ok, :error) { |value| # ... }` will execute the block if the result type is `:ok` or `:error`.
585
+
586
+ - Add `BCDD::Result#on_success` to execute a block if the result is a success. It works like `BCDD::Result#on` but only for success results.
587
+
588
+ - Add `BCDD::Result#on_failure` to execute a block if the result is a failure. It works like `BCDD::Result#on` but only for failure results.
589
+
590
+ - Add `BCDD::Result#and_then` to execute the block if the result is a success. You can use it to chain multiple operations. If the block returns a failure result and there are other `and_then` calls after it, the next blocks will be skipped.
591
+
592
+ - Add `BCDD::Result#data` as an alias for `BCDD::Result#value`.
593
+
594
+ - Add `BCDD::Result#data_or` as an alias for `BCDD::Result#value_or`.
595
+
596
+ - Add `BCDD::Result#on_type` as an alias for `BCDD::Result#on`.
597
+
598
+ - Add `BCDD::Result::Success()` to factory a success result.
599
+
600
+ - Add `BCDD::Result::Failure()` to factory a failure result.
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at rodrigo.serradura@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.