bcdd-result 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96c58d6a9132fbf42fc8f3c1d9ba683f0b6297e23b1f0536fc4bcb7efb5084df
4
- data.tar.gz: fcef45f3c5ddacc20ff5e9533012c7fbe5817878cef1ff528dabd8920e1cbccd
3
+ metadata.gz: 0a6bfbf4821c7674fd0af6dbd4e88d274cbf14595eff278ef40ebdaf5bf4490a
4
+ data.tar.gz: 9cc905974762089c3f3827ac40aa730040ad9e88176e8cae0275954bedd6e7a8
5
5
  SHA512:
6
- metadata.gz: d4cf048907571205c6cd0b90ca4b95c2480c34de79d082132e4f1cab1e809f4c6eec538d9d38b036ad313d9653420625b68a502272e73371b99b157f43315123
7
- data.tar.gz: 4662aaeb33a7eb770ab51529cc1f2c4e9d86844dec70dfcdc4f37674aab33d010d6cbe7932fc635f09979d47b4b631748a3d4791021e99108a011a93469a2d73
6
+ metadata.gz: 5446879d905a42e257b3dd5724b6dc53a455cf2ec8e3fd7a5f339f74216e8d87ce779912dd4124163a6af4a19052e558382e74e7b20a434420b8d2eae055ab37
7
+ data.tar.gz: c91588b64905a6d86b84dc04db128e42fb0c570cf1e973ccd76563b0126e0e30d31764e918aa93aa71b06115a95c984299d4a57c249391d366d8738469aa8ecb
data/.rubocop.yml CHANGED
@@ -20,7 +20,7 @@ Style/ClassAndModuleChildren:
20
20
 
21
21
  Naming/MethodName:
22
22
  Exclude:
23
- - lib/bcdd/resultable.rb
23
+ - lib/bcdd/result/mixin.rb
24
24
 
25
25
  Minitest/MultipleAssertions:
26
26
  Enabled: false
data/.rubocop_todo.yml CHANGED
@@ -1,21 +1,22 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2023-09-27 00:47:03 UTC using RuboCop version 1.56.3.
3
+ # on 2023-09-29 01:50:30 UTC using RuboCop version 1.56.3.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 13
9
+ # Offense count: 14
10
10
  # Configuration parameters: AllowedConstants.
11
11
  Style/Documentation:
12
12
  Exclude:
13
13
  - 'spec/**/*'
14
14
  - 'test/**/*'
15
15
  - 'lib/bcdd/result.rb'
16
+ - 'lib/bcdd/result/data.rb'
16
17
  - 'lib/bcdd/result/error.rb'
17
18
  - 'lib/bcdd/result/failure.rb'
18
19
  - 'lib/bcdd/result/handler.rb'
20
+ - 'lib/bcdd/result/mixin.rb'
19
21
  - 'lib/bcdd/result/success.rb'
20
22
  - 'lib/bcdd/result/type.rb'
21
- - 'lib/bcdd/resultable.rb'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2023-09-28
4
+
5
+ ### Added
6
+
7
+ - Add `require 'result'` to define `Result` as an alias for `BCDD::Result`.
8
+
9
+ - Add support to pattern matching (Ruby 2.7+).
10
+
11
+ - 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.
12
+
13
+ - 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.
14
+
15
+ ### Changed
16
+
17
+ - **(BREAKING)** Rename `BCDD::Resultable` to `BCDD::Result::Mixin`.
18
+
19
+ - **(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`).
20
+
21
+ ### Removed
22
+
23
+ - **(BREAKING)** Remove `BCDD::Result#data_or`.
24
+
3
25
  ## [0.3.0] - 2023-09-26
4
26
 
5
27
  ### Added
data/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  <p align="center">
2
2
  <h1 align="center" id="-bcddresult">🔀 BCDD::Result</h1>
3
3
  <p align="center"><i>Empower Ruby apps with a pragmatic use of Railway Oriented Programming.</i></p>
4
- <br>
4
+ <p align="center">
5
+ <img src="https://img.shields.io/badge/ruby->%3D%202.7.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
6
+ <img src="https://badge.fury.io/rb/bcdd-result.svg" alt="bcdd-result gem version" height="18">
7
+ </p>
5
8
  </p>
6
9
 
7
- A general-purpose result monad that allows you to create objects that represent a success (`BCDD::Result::Success`) or failure (`BCDD::Result::Failure`).
10
+ It's a general-purpose result monad that allows you to create objects representing a success (`BCDD::Result::Success`) or failure (`BCDD::Result::Failure`).
8
11
 
9
12
  **What problem does it solve?**
10
13
 
@@ -17,6 +20,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
17
20
  - [Ruby Version](#ruby-version)
18
21
  - [Installation](#installation)
19
22
  - [Usage](#usage)
23
+ - [`BCDD::Result` *versus* `Result`](#bcddresult-versus-result)
20
24
  - [Reference](#reference)
21
25
  - [Result Attributes](#result-attributes)
22
26
  - [Receiving types in `result.success?` or `result.failure?`](#receiving-types-in-resultsuccess-or-resultfailure)
@@ -25,16 +29,21 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
25
29
  - [`result.on_type`](#resulton_type)
26
30
  - [`result.on_success`](#resulton_success)
27
31
  - [`result.on_failure`](#resulton_failure)
32
+ - [`result.on_unknown`](#resulton_unknown)
28
33
  - [`result.handle`](#resulthandle)
29
34
  - [Result Value](#result-value)
30
35
  - [`result.value_or`](#resultvalue_or)
31
- - [`result.data_or`](#resultdata_or)
36
+ - [Result Data](#result-data)
37
+ - [`result.data`](#resultdata)
32
38
  - [Railway Oriented Programming](#railway-oriented-programming)
33
39
  - [`result.and_then`](#resultand_then)
34
- - [`BCDD::Resultable`](#bcddresultable)
40
+ - [`BCDD::Result::Mixin`](#bcddresultmixin)
35
41
  - [Class example (instance methods)](#class-example-instance-methods)
36
42
  - [Module example (singleton methods)](#module-example-singleton-methods)
37
43
  - [Restrictions](#restrictions)
44
+ - [Pattern Matching](#pattern-matching)
45
+ - [`Array`/`Find` patterns](#arrayfind-patterns)
46
+ - [`Hash` patterns](#hash-patterns)
38
47
  - [About](#about)
39
48
  - [Development](#development)
40
49
  - [Contributing](#contributing)
@@ -61,7 +70,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
61
70
 
62
71
  $ gem install bcdd-result
63
72
 
64
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
73
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
65
74
 
66
75
  ## Usage
67
76
 
@@ -81,7 +90,23 @@ BCDD::Result::Success(:ok) #
81
90
  BCDD::Result::Failure(:err) #
82
91
  ```
83
92
 
84
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
93
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
94
+
95
+ #### `BCDD::Result` *versus* `Result`
96
+
97
+ The `BCDD::Result` is the main module of this gem. It contains all the features, constants, and methods you will use to create and manipulate results.
98
+
99
+ The `Result` is an alias of `BCDD::Result`. It was created to facilitate the use of this gem in the code. So, instead of requiring `BCDD::Result` everywhere, you can require `Result` and use it as an alias.
100
+
101
+ ```ruby
102
+ require 'result'
103
+
104
+ Result::Success(:ok) # <BCDD::Result::Success type=:ok value=nil>
105
+ ```
106
+
107
+ All the examples in this README that use `BCDD::Result` can also be used with `Result`.
108
+
109
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
85
110
 
86
111
  ## Reference
87
112
 
@@ -137,7 +162,7 @@ result.type # :no
137
162
  result.value # nil
138
163
  ```
139
164
 
140
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
165
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
141
166
 
142
167
  #### Receiving types in `result.success?` or `result.failure?`
143
168
 
@@ -163,7 +188,7 @@ result.failure?(:err) # true
163
188
  result.failure?(:error) # false
164
189
  ```
165
190
 
166
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
191
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
167
192
 
168
193
  ### Result Hooks
169
194
 
@@ -182,7 +207,7 @@ def divide(arg1, arg2)
182
207
  end
183
208
  ```
184
209
 
185
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
210
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
186
211
 
187
212
  #### `result.on`
188
213
 
@@ -220,7 +245,7 @@ result.object_id == output.object_id # true
220
245
 
221
246
  *PS: The `divide()` implementation is [here](#result-hooks).*
222
247
 
223
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
248
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
224
249
 
225
250
  #### `result.on_type`
226
251
 
@@ -249,7 +274,7 @@ divide(4, 4).on_failure { |error| puts error }
249
274
 
250
275
  *PS: The `divide()` implementation is [here](#result-hooks).*
251
276
 
252
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
277
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
253
278
 
254
279
  #### `result.on_failure`
255
280
 
@@ -271,7 +296,28 @@ divide(4, 0).on_failure(:invalid_arg) { |error| puts error }
271
296
 
272
297
  *PS: The `divide()` implementation is [here](#result-hooks).*
273
298
 
274
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
299
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
300
+
301
+ #### `result.on_unknown`
302
+
303
+ `BCDD::Result#on_unknown` will perform the block when no other hook (`#on`, `#on_type`, `#on_failure`, `#on_success`) has been executed.
304
+
305
+ Regardless of the block being executed, the return of the method will always be the result itself.
306
+
307
+ The result value will be exposed as the first argument of the block.
308
+
309
+ ```ruby
310
+ divide(4, 2)
311
+ .on(:invalid_arg) { |msg| puts msg }
312
+ .on(:division_by_zero) { |msg| puts msg }
313
+ .on_unknown { |value, type| puts [type, value].inspect }
314
+
315
+ # The code above will print '[:division_completed, 2]' and return the result itself.
316
+ ```
317
+
318
+ *PS: The `divide()` implementation is [here](#result-hooks).*
319
+
320
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
275
321
 
276
322
  #### `result.handle`
277
323
 
@@ -282,6 +328,7 @@ divide(4, 2).handle do |result|
282
328
  result.success { |number| number }
283
329
  result.failure(:invalid_arg) { |err| puts err }
284
330
  result.type(:division_by_zero) { raise ZeroDivisionError }
331
+ result.unknown { raise NotImplementedError }
285
332
  end
286
333
 
287
334
  #or
@@ -289,6 +336,7 @@ end
289
336
  divide(4, 2).handle do |on|
290
337
  on.success { |number| number }
291
338
  on.failure { |err| puts err }
339
+ on.unknown { raise NotImplementedError }
292
340
  end
293
341
 
294
342
  #or
@@ -297,6 +345,7 @@ divide(4, 2).handle do |on|
297
345
  on.type(:invalid_arg) { |err| puts err }
298
346
  on.type(:division_by_zero) { raise ZeroDivisionError }
299
347
  on.type(:division_completed) { |number| number }
348
+ on.unknown { raise NotImplementedError }
300
349
  end
301
350
 
302
351
  # or
@@ -305,6 +354,7 @@ divide(4, 2).handle do |on|
305
354
  on[:invalid_arg] { |err| puts err }
306
355
  on[:division_by_zero] { raise ZeroDivisionError }
307
356
  on[:division_completed] { |number| number }
357
+ on.unknown { raise NotImplementedError }
308
358
  end
309
359
 
310
360
  # The [] syntax 👆 is an alias of #type.
@@ -317,7 +367,7 @@ end
317
367
 
318
368
  *PS: The `divide()` implementation is [here](#result-hooks).*
319
369
 
320
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
370
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
321
371
 
322
372
  ### Result Value
323
373
 
@@ -350,11 +400,50 @@ divide(100, 0).value_or { 0 } # 0
350
400
 
351
401
  *PS: The `divide()` implementation is [here](#result-hooks).*
352
402
 
353
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
403
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
404
+
405
+ ### Result Data
406
+
407
+ #### `result.data`
408
+
409
+ The `BCDD::Result#data` exposes the result attributes (name, type, value) directly and as a hash (`to_h`/`to_hash`) and array (`to_a`/`to_ary`).
410
+
411
+ This is helpful if you need to access the result attributes generically or want to use Ruby features like splat (`*`) and double splat (`**`) operators.
412
+
413
+ See the examples below to understand how to use it.
414
+
415
+ ```ruby
416
+ result = BCDD::Result::Success(:ok, 1)
417
+
418
+ success_data = result.data # #<BCDD::Result::Data name=:success type=:ok value=1>
419
+
420
+ success_data.name # :success
421
+ success_data.type # :ok
422
+ success_data.value # 1
423
+
424
+ success_data.to_h # {:name=>:success, :type=>:ok, :value=>1}
425
+ success_data.to_a # [:success, :ok, 1]
426
+
427
+ name, type, value = success_data
428
+
429
+ [name, type, value] # [:success, :ok, 1]
430
+
431
+ def print_to_ary(name, type, value)
432
+ puts [name, type, value].inspect
433
+ end
434
+
435
+ def print_to_hash(name:, type:, value:)
436
+ puts [name, type, value].inspect
437
+ end
438
+
439
+ print_to_ary(*success_data) # [:success, :ok, 1]
354
440
 
355
- #### `result.data_or`
441
+ print_to_hash(**success_data) # [:success, :ok, 1]
442
+ ```
356
443
 
357
- `BCDD::Result#data_or` is an alias of `BCDD::Result#value_or`.
444
+ > **NOTE:** The example above uses a success result, but the same is valid for a failure result.
445
+
446
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
358
447
 
359
448
  ### Railway Oriented Programming
360
449
 
@@ -416,9 +505,9 @@ Divide.call(2, 2)
416
505
  #<BCDD::Result::Success type=:division_completed data=1>
417
506
  ```
418
507
 
419
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
508
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
420
509
 
421
- #### `BCDD::Resultable`
510
+ #### `BCDD::Result::Mixin`
422
511
 
423
512
  It is a module that can be included/extended by any object. It adds two methods to the target object: `Success()` and `Failure()`.
424
513
 
@@ -430,7 +519,7 @@ And because of this, you can use the `#and_then` method to call methods from the
430
519
 
431
520
  ```ruby
432
521
  class Divide
433
- include BCDD::Resultable
522
+ include BCDD::Result::Mixin
434
523
 
435
524
  attr_reader :arg1, :arg2
436
525
 
@@ -478,7 +567,7 @@ Divide.new(4, '2').call #<BCDD::Result::Failure type=:invalid_arg value="arg2 mu
478
567
 
479
568
  ```ruby
480
569
  module Divide
481
- extend BCDD::Resultable
570
+ extend BCDD::Result::Mixin
482
571
  extend self
483
572
 
484
573
  def call(arg1, arg2)
@@ -518,17 +607,78 @@ Divide.call(4, '2') #<BCDD::Result::Failure type=:invalid_arg value="arg2 must b
518
607
 
519
608
  The unique condition for using the `#and_then` to call methods is that they must use the `Success()` and `Failure()` to produce their results.
520
609
 
521
- If you use `BCDD::Result::Subject()`/`BCDD::Result::Failure()`, or call another `BCDD::Resultable` object, the `#and_then` will raise an error because the subjects will be different.
610
+ If you use `BCDD::Result::Subject()`/`BCDD::Result::Failure()`, or use result from another `BCDD::Result::Mixin` instance, the `#and_then` will raise an error because the subjects will be different.
522
611
 
523
612
  > **Note**: You still can use the block syntax, but all the results must be produced by the subject's `Success()` and `Failure()` methods.
524
613
 
525
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
614
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
615
+
616
+ ### Pattern Matching
617
+
618
+ The `BCDD::Result` also provides support to pattern matching.
619
+
620
+ In the further examples, I will use the `Divide` lambda to exemplify its usage.
621
+
622
+ ```ruby
623
+ Divide = lambda do |arg1, arg2|
624
+ arg1.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg1 must be numeric')
625
+ arg2.is_a?(::Numeric) or return BCDD::Result::Failure(:invalid_arg, 'arg2 must be numeric')
626
+
627
+ return BCDD::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?
628
+
629
+ BCDD::Result::Success(:division_completed, arg1 / arg2)
630
+ end
631
+ ```
632
+
633
+ #### `Array`/`Find` patterns
634
+
635
+ ```ruby
636
+ case Divide.call(4, 2)
637
+ in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
638
+ in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
639
+ in BCDD::Result::Success[:division_completed, value] then puts value
640
+ end
641
+
642
+ # The code above will print: 2
643
+
644
+ case Divide.call(4, 0)
645
+ in BCDD::Result::Failure[:invalid_arg, msg] then puts msg
646
+ in BCDD::Result::Failure[:division_by_zero, msg] then puts msg
647
+ in BCDD::Result::Success[:division_completed, value] then puts value
648
+ end
649
+
650
+ # The code above will print: arg2 must not be zero
651
+ ```
652
+
653
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
654
+
655
+ #### `Hash` patterns
656
+
657
+ ```ruby
658
+ case Divide.call(10, 2)
659
+ in { failure: { invalid_arg: msg } } then puts msg
660
+ in { failure: { division_by_zero: msg } } then puts msg
661
+ in { success: { division_completed: value } } then puts value
662
+ end
663
+
664
+ # The code above will print: 5
665
+
666
+ case Divide.call('10', 2)
667
+ in { failure: { invalid_arg: msg } } then puts msg
668
+ in { failure: { division_by_zero: msg } } then puts msg
669
+ in { success: { division_completed: value } } then puts value
670
+ end
671
+
672
+ # The code above will print: arg1 must be numeric
673
+ ```
674
+
675
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
526
676
 
527
677
  ## About
528
678
 
529
679
  [Rodrigo Serradura](https://github.com/serradura) created this project. He is the B/CDD process/method creator and has already made similar gems like the [u-case](https://github.com/serradura/u-case) and [kind](https://github.com/serradura/kind/blob/main/lib/kind/result.rb). This gem is a general-purpose abstraction/monad, but it also contains key features that serve as facilitators for adopting B/CDD in the code.
530
680
 
531
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
681
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
532
682
 
533
683
  ## Development
534
684
 
@@ -536,19 +686,19 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
536
686
 
537
687
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
538
688
 
539
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
689
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
540
690
 
541
691
  ## Contributing
542
692
 
543
693
  Bug reports and pull requests are welcome on GitHub at https://github.com/B-CDD/bcdd-result. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/B-CDD/bcdd-result/blob/master/CODE_OF_CONDUCT.md).
544
694
 
545
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
695
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
546
696
 
547
697
  ## License
548
698
 
549
699
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
550
700
 
551
- <p align="right">(<a href="#-bcddresult">⬆️ &nbsp;back to top</a>)</p>
701
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
552
702
 
553
703
  ## Code of Conduct
554
704
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BCDD::Result
4
+ class Data
5
+ attr_reader :name, :type, :value, :to_h, :to_a
6
+
7
+ def initialize(result)
8
+ @name = result.send(:name)
9
+ @type = result.type
10
+ @value = result.value
11
+
12
+ @to_h = { name: name, type: type, value: value }
13
+ @to_a = [name, type, value]
14
+ end
15
+
16
+ def inspect
17
+ format(
18
+ '#<%<class_name>s name=%<name>p type=%<type>p value=%<value>p>',
19
+ class_name: self.class.name, name: name, type: type, value: value
20
+ )
21
+ end
22
+
23
+ alias to_ary to_a
24
+ alias to_hash to_h
25
+ end
26
+
27
+ private_constant :Data
28
+ end
@@ -1,46 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class BCDD::Result
4
- class Error < ::StandardError
5
- def self.build(**_kargs)
6
- new
7
- end
3
+ class BCDD::Result::Error < StandardError
4
+ def self.build(**_kargs)
5
+ new
6
+ end
8
7
 
9
- class NotImplemented < self
10
- end
8
+ class NotImplemented < self
9
+ end
11
10
 
12
- class MissingTypeArgument < self
13
- def initialize(_arg = nil)
14
- super('A type (argument) is required to invoke the #on/#on_type method')
15
- end
11
+ class MissingTypeArgument < self
12
+ def initialize(_arg = nil)
13
+ super('A type (argument) is required to invoke the #on/#on_type method')
16
14
  end
15
+ end
17
16
 
18
- class UnexpectedOutcome < self
19
- def self.build(outcome:, origin:)
20
- message =
21
- "Unexpected outcome: #{outcome.inspect}. The #{origin} must return this object wrapped by " \
22
- 'BCDD::Result::Success or BCDD::Result::Failure'
17
+ class UnexpectedOutcome < self
18
+ def self.build(outcome:, origin:)
19
+ message =
20
+ "Unexpected outcome: #{outcome.inspect}. The #{origin} must return this object wrapped by " \
21
+ 'BCDD::Result::Success or BCDD::Result::Failure'
23
22
 
24
- new(message)
25
- end
23
+ new(message)
26
24
  end
25
+ end
27
26
 
28
- class WrongResultSubject < self
29
- def self.build(given_result:, expected_subject:)
30
- message =
31
- "You cannot call #and_then and return a result that does not belong to the subject!\n" \
32
- "Expected subject: #{expected_subject.inspect}\n" \
33
- "Given subject: #{given_result.send(:subject).inspect}\n" \
34
- "Given result: #{given_result.inspect}"
27
+ class WrongResultSubject < self
28
+ def self.build(given_result:, expected_subject:)
29
+ message =
30
+ "You cannot call #and_then and return a result that does not belong to the subject!\n" \
31
+ "Expected subject: #{expected_subject.inspect}\n" \
32
+ "Given subject: #{given_result.send(:subject).inspect}\n" \
33
+ "Given result: #{given_result.inspect}"
35
34
 
36
- new(message)
37
- end
35
+ new(message)
38
36
  end
37
+ end
39
38
 
40
- class WrongSubjectMethodArity < self
41
- def self.build(subject:, method:)
42
- new("#{subject.class}##{method.name} has unsupported arity (#{method.arity}). Expected 0 or 1.")
43
- end
39
+ class WrongSubjectMethodArity < self
40
+ def self.build(subject:, method:)
41
+ new("#{subject.class}##{method.name} has unsupported arity (#{method.arity}). Expected 0 or 1.")
44
42
  end
45
43
  end
46
44
  end
@@ -14,7 +14,11 @@ class BCDD::Result
14
14
  yield
15
15
  end
16
16
 
17
- alias data_or value_or
17
+ private
18
+
19
+ def name
20
+ :failure
21
+ end
18
22
  end
19
23
 
20
24
  def self.Failure(type, value = nil)
@@ -25,6 +25,10 @@ class BCDD::Result
25
25
  self.outcome = block if result.success? && _type.in?(types, allow_empty: true)
26
26
  end
27
27
 
28
+ def unknown(&block)
29
+ self.outcome = block unless outcome?
30
+ end
31
+
28
32
  alias type []
29
33
 
30
34
  private
@@ -35,13 +39,13 @@ class BCDD::Result
35
39
  @outcome != UNDEFINED
36
40
  end
37
41
 
38
- def outcome
39
- @outcome if outcome?
40
- end
41
-
42
42
  def outcome=(block)
43
43
  @outcome = block.call(result.value, result.type) unless outcome?
44
44
  end
45
+
46
+ def outcome
47
+ @outcome if outcome?
48
+ end
45
49
  end
46
50
 
47
51
  private_constant :Handler
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BCDD::Result
4
+ module Mixin
5
+ def Success(type, value = nil)
6
+ Success.new(type: type, value: value, subject: self)
7
+ end
8
+
9
+ def Failure(type, value = nil)
10
+ Failure.new(type: type, value: value, subject: self)
11
+ end
12
+ end
13
+ end
@@ -14,7 +14,11 @@ class BCDD::Result
14
14
  value
15
15
  end
16
16
 
17
- alias data_or value_or
17
+ private
18
+
19
+ def name
20
+ :success
21
+ end
18
22
  end
19
23
 
20
24
  def self.Success(type, value = nil)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module BCDD
4
4
  class Result
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
data/lib/bcdd/result.rb CHANGED
@@ -3,21 +3,27 @@
3
3
  require_relative 'result/version'
4
4
  require_relative 'result/error'
5
5
  require_relative 'result/type'
6
+ require_relative 'result/data'
6
7
  require_relative 'result/handler'
7
8
  require_relative 'result/failure'
8
9
  require_relative 'result/success'
9
-
10
- require_relative 'resultable'
10
+ require_relative 'result/mixin'
11
11
 
12
12
  class BCDD::Result
13
+ attr_accessor :unknown
14
+
13
15
  attr_reader :_type, :value, :subject
14
16
 
15
17
  protected :subject
16
18
 
19
+ private :unknown, :unknown=
20
+
17
21
  def initialize(type:, value:, subject: nil)
18
22
  @_type = Type.new(type)
19
23
  @value = value
20
24
  @subject = subject
25
+
26
+ self.unknown = true
21
27
  end
22
28
 
23
29
  def type
@@ -36,31 +42,22 @@ class BCDD::Result
36
42
  raise Error::NotImplemented
37
43
  end
38
44
 
39
- def ==(other)
40
- self.class == other.class && type == other.type && value == other.value
41
- end
42
- alias eql? ==
43
-
44
- def hash
45
- [self.class, type, value].hash
46
- end
45
+ def on(*types, &block)
46
+ raise Error::MissingTypeArgument if types.empty?
47
47
 
48
- def inspect
49
- format('#<%<class_name>s type=%<type>p value=%<value>p>', class_name: self.class.name, type: type, value: value)
48
+ tap { known(block) if _type.in?(types, allow_empty: false) }
50
49
  end
51
50
 
52
- def on(*types)
53
- raise Error::MissingTypeArgument if types.empty?
54
-
55
- tap { yield(value, type) if _type.in?(types, allow_empty: false) }
51
+ def on_success(*types, &block)
52
+ tap { known(block) if success? && _type.in?(types, allow_empty: true) }
56
53
  end
57
54
 
58
- def on_success(*types)
59
- tap { yield(value, type) if success? && _type.in?(types, allow_empty: true) }
55
+ def on_failure(*types, &block)
56
+ tap { known(block) if failure? && _type.in?(types, allow_empty: true) }
60
57
  end
61
58
 
62
- def on_failure(*types)
63
- tap { yield(value, type) if failure? && _type.in?(types, allow_empty: true) }
59
+ def on_unknown
60
+ tap { yield(value, type) if unknown }
64
61
  end
65
62
 
66
63
  def and_then(method_name = nil)
@@ -81,12 +78,45 @@ class BCDD::Result
81
78
  handler.send(:outcome)
82
79
  end
83
80
 
84
- alias data value
85
- alias data_or value_or
81
+ def data
82
+ Data.new(self)
83
+ end
84
+
85
+ def ==(other)
86
+ self.class == other.class && type == other.type && value == other.value
87
+ end
88
+
89
+ def hash
90
+ [self.class, type, value].hash
91
+ end
92
+
93
+ def inspect
94
+ format('#<%<class_name>s type=%<type>p value=%<value>p>', class_name: self.class.name, type: type, value: value)
95
+ end
96
+
97
+ def deconstruct
98
+ [type, value]
99
+ end
100
+
101
+ def deconstruct_keys(_keys)
102
+ { name => { type => value } }
103
+ end
104
+
105
+ alias eql? ==
86
106
  alias on_type on
87
107
 
88
108
  private
89
109
 
110
+ def name
111
+ raise Error::NotImplemented
112
+ end
113
+
114
+ def known(block)
115
+ self.unknown = false
116
+
117
+ block.call(value, type)
118
+ end
119
+
90
120
  def call_subject_method(method_name)
91
121
  method = subject.method(method_name)
92
122
 
data/lib/result.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'bcdd/result'
4
+
5
+ Object.const_set(:Result, BCDD::Result)
data/sig/bcdd/result.rbs CHANGED
@@ -5,6 +5,8 @@ module BCDD
5
5
  end
6
6
 
7
7
  class BCDD::Result
8
+ private attr_accessor unknown: bool
9
+
8
10
  attr_reader _type: BCDD::Result::Type
9
11
  attr_reader value: untyped
10
12
  attr_reader subject: untyped
@@ -18,27 +20,31 @@ class BCDD::Result
18
20
 
19
21
  def value_or: { () -> untyped } -> untyped
20
22
 
21
- def ==: (untyped) -> bool
22
- alias eql? ==
23
-
24
- def hash: -> Integer
25
-
26
- def inspect: -> String
27
-
28
23
  def on: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
29
24
  def on_success: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
30
25
  def on_failure: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
26
+ def on_unknown: () { (untyped, Symbol) -> void } -> BCDD::Result
31
27
 
32
28
  def and_then: (?Symbol method_name) { (untyped) -> untyped } -> BCDD::Result
33
29
 
34
30
  def handle: { (BCDD::Result::Handler) -> void } -> untyped
35
31
 
36
- alias data value
37
- alias data_or value_or
32
+ def data: -> BCDD::Result::Data
33
+
34
+ def ==: (untyped) -> bool
35
+ def hash: -> Integer
36
+ def inspect: -> String
37
+
38
+ def deconstruct: -> [Symbol, [Symbol, untyped]]
39
+ def deconstruct_keys: (Array[Symbol]) -> Hash[Symbol, Hash[Symbol, untyped]]
40
+
41
+ alias eql? ==
38
42
  alias on_type on
39
43
 
40
44
  private
41
45
 
46
+ def name: -> Symbol
47
+ def known: (Proc) -> untyped
42
48
  def call_subject_method: (Symbol) -> BCDD::Result
43
49
  def ensure_result_object: (untyped, origin: Symbol) -> BCDD::Result
44
50
  end
@@ -57,6 +63,14 @@ class BCDD::Result
57
63
  def self.Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
58
64
  end
59
65
 
66
+ class BCDD::Result
67
+ module Mixin
68
+ def Success: (Symbol type, ?untyped value) -> BCDD::Result::Success
69
+
70
+ def Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
71
+ end
72
+ end
73
+
60
74
  class BCDD::Result
61
75
  class Handler
62
76
  UNDEFINED: Object
@@ -66,6 +80,7 @@ class BCDD::Result
66
80
  def []: (*Symbol) { (untyped, Symbol) -> void } -> untyped
67
81
  def failure: (*Symbol) { (untyped, Symbol) -> void } -> untyped
68
82
  def success: (*Symbol) { (untyped, Symbol) -> void } -> untyped
83
+ def unknown: () { (untyped, Symbol) -> void } -> untyped
69
84
 
70
85
  alias type []
71
86
 
@@ -75,18 +90,8 @@ class BCDD::Result
75
90
  attr_reader result: BCDD::Result
76
91
 
77
92
  def outcome?: -> bool
78
-
79
- def outcome: -> untyped
80
-
81
93
  def outcome=: (Proc) -> void
82
- end
83
- end
84
-
85
- module BCDD
86
- module Resultable
87
- def Success: (Symbol type, ?untyped value) -> BCDD::Result::Success
88
-
89
- def Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
94
+ def outcome: -> untyped
90
95
  end
91
96
  end
92
97
 
@@ -100,6 +105,23 @@ class BCDD::Result
100
105
  end
101
106
  end
102
107
 
108
+ class BCDD::Result
109
+ class Data
110
+ attr_reader name: Symbol
111
+ attr_reader type: Symbol
112
+ attr_reader value: untyped
113
+ attr_reader to_h: Hash[Symbol, untyped]
114
+ attr_reader to_a: [Symbol, Symbol, untyped]
115
+
116
+ def initialize: (BCDD::Result) -> void
117
+
118
+ def inspect: -> String
119
+
120
+ alias to_ary to_a
121
+ alias to_hash to_h
122
+ end
123
+ end
124
+
103
125
  class BCDD::Result
104
126
  class Error < ::StandardError
105
127
  def self.build: (**untyped) -> BCDD::Result::Error
metadata CHANGED
@@ -1,17 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcdd-result
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-27 00:00:00.000000000 Z
11
+ date: 2023-09-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: A general-purpose result monad that allows you to create objects that
14
- represent a success (BCDD::Result::Success) or failure (BCDD::Result::Failure).
13
+ description: |-
14
+ Empower Ruby apps with a pragmatic use of Railway Oriented Programming.
15
+
16
+ It's a general-purpose result monad that allows you to create objects representing a success (BCDD::Result::Success) or failure (BCDD::Result::Failure).
15
17
  email:
16
18
  - rodrigo.serradura@gmail.com
17
19
  executables: []
@@ -27,13 +29,15 @@ files:
27
29
  - Rakefile
28
30
  - Steepfile
29
31
  - lib/bcdd/result.rb
32
+ - lib/bcdd/result/data.rb
30
33
  - lib/bcdd/result/error.rb
31
34
  - lib/bcdd/result/failure.rb
32
35
  - lib/bcdd/result/handler.rb
36
+ - lib/bcdd/result/mixin.rb
33
37
  - lib/bcdd/result/success.rb
34
38
  - lib/bcdd/result/type.rb
35
39
  - lib/bcdd/result/version.rb
36
- - lib/bcdd/resultable.rb
40
+ - lib/result.rb
37
41
  - sig/bcdd/result.rbs
38
42
  homepage: https://github.com/b-cdd/result
39
43
  licenses:
@@ -62,5 +66,5 @@ requirements: []
62
66
  rubygems_version: 3.4.19
63
67
  signing_key:
64
68
  specification_version: 4
65
- summary: A result abstraction (monad based) for Ruby.
69
+ summary: A pragmatic result abstraction (monad based) for Ruby.
66
70
  test_files: []
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module BCDD::Resultable
4
- def Success(type, value = nil)
5
- BCDD::Result::Success.new(type: type, value: value, subject: self)
6
- end
7
-
8
- def Failure(type, value = nil)
9
- BCDD::Result::Failure.new(type: type, value: value, subject: self)
10
- end
11
- end