bcdd-result 0.8.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b49559a4258169692f89f3263c14485cde2e17e36c90ccde9bf88dab355d458
4
- data.tar.gz: b62c215886f572f942a752767db89544ae5e483a496975882fcc9108d99edc0f
3
+ metadata.gz: 6e4fa0b7a751971ae5fd7987906342e6436535b9bb8729252c7651a5c533302c
4
+ data.tar.gz: c2f2359525d2c24e28da5d400ef3e7284ccb2c13b8955da84cddb8da7b229cb3
5
5
  SHA512:
6
- metadata.gz: 2794daec03ab43d9d9549887dae87d0138195f0c85f65ddcc1015d74a9a940c639e7dc73086c9f948de00c8ce95d576105a3ad9e671b068d56d39e4ef075c3ff
7
- data.tar.gz: 197572b1a2dc61a72de744af1bbb50a642731dc4e248133164da36fb8ff8d257fe89c2874798ea9f77533aa462e386dd002cc2dd25f241ca12cd02ff8d080fa4
6
+ metadata.gz: 1fba0932c35635a248eeef6c88b8c386ce2f8a55f235784ea6c144299a0a17f8501efa16f5a171babd2a883b90a8d06726bd47bfcb2261196d726068b8d6e097
7
+ data.tar.gz: 66722933fdb17f8317c0989ee103a38479b49ffb151ac23199ad91669a48a693af9ccce2b863dda913c54bc3b9d446ce802b0b4221352dffd68a08de4c647a23
data/.rubocop.yml CHANGED
@@ -58,6 +58,7 @@ Metrics/BlockLength:
58
58
 
59
59
  Metrics/ClassLength:
60
60
  Exclude:
61
+ - lib/bcdd/result.rb
61
62
  - test/**/*.rb
62
63
 
63
64
  Minitest/MultipleAssertions:
data/CHANGELOG.md CHANGED
@@ -1,30 +1,52 @@
1
1
  - [\[Unreleased\]](#unreleased)
2
- - [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
2
+ - [\[0.9.0\] - 2023-12-12](#090---2023-12-12)
3
3
  - [Added](#added)
4
4
  - [Changed](#changed)
5
- - [Removed](#removed)
6
- - [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
5
+ - [\[0.8.0\] - 2023-12-11](#080---2023-12-11)
7
6
  - [Added](#added-1)
8
7
  - [Changed](#changed-1)
9
- - [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
8
+ - [Removed](#removed)
9
+ - [\[0.7.0\] - 2023-10-27](#070---2023-10-27)
10
10
  - [Added](#added-2)
11
11
  - [Changed](#changed-2)
12
- - [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
12
+ - [\[0.6.0\] - 2023-10-11](#060---2023-10-11)
13
13
  - [Added](#added-3)
14
- - [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
15
- - [Added](#added-4)
16
14
  - [Changed](#changed-3)
15
+ - [\[0.5.0\] - 2023-10-09](#050---2023-10-09)
16
+ - [Added](#added-4)
17
+ - [\[0.4.0\] - 2023-09-28](#040---2023-09-28)
18
+ - [Added](#added-5)
19
+ - [Changed](#changed-4)
17
20
  - [Removed](#removed-1)
18
21
  - [\[0.3.0\] - 2023-09-26](#030---2023-09-26)
19
- - [Added](#added-5)
20
- - [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
21
22
  - [Added](#added-6)
23
+ - [\[0.2.0\] - 2023-09-26](#020---2023-09-26)
24
+ - [Added](#added-7)
22
25
  - [Removed](#removed-2)
23
26
  - [\[0.1.0\] - 2023-09-25](#010---2023-09-25)
24
- - [Added](#added-7)
27
+ - [Added](#added-8)
25
28
 
26
29
  ## [Unreleased]
27
30
 
31
+ ## [0.9.0] - 2023-12-12
32
+
33
+ ### Added
34
+
35
+ - Add new `BCDD::Result.config.constant_alias` options. `Context` and `BCDD::Context` are now available as aliases for `BCDD::Result::Context`.
36
+ ```ruby
37
+ BCDD::Result.config.constant_alias.enable!('Context')
38
+
39
+ BCDD::Result.config.constant_alias.enable!('BCDD::Context')
40
+ ```
41
+
42
+ - 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.
43
+
44
+ ### Changed
45
+
46
+ - **(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)
47
+
48
+ - **(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).
49
+
28
50
  ## [0.8.0] - 2023-12-11
29
51
 
30
52
  ### Added
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <h1 align="center" id="-bcddresult">🔀 BCDD::Result</h1>
3
- <p align="center"><i>Empower Ruby apps with pragmatic use of Result monad, Railway Oriented Programming, and B/CDD.</i></p>
3
+ <p align="center"><i>Empower Ruby apps with pragmatic use of Result pattern (monad), Railway Oriented Programming, and B/CDD.</i></p>
4
4
  <p align="center">
5
5
  <img src="https://img.shields.io/badge/ruby->%3D%202.7.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
6
6
  <a href="https://rubygems.org/gems/bcdd-result"><img src="https://badge.fury.io/rb/bcdd-result.svg" alt="bcdd-result gem version" height="18"></a>
@@ -63,6 +63,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
63
63
  - [`BCDD::Result::Expectations.mixin` add-ons](#bcddresultexpectationsmixin-add-ons)
64
64
  - [`BCDD::Result::Context`](#bcddresultcontext)
65
65
  - [Defining successes and failures](#defining-successes-and-failures)
66
+ - [Constant aliases](#constant-aliases)
66
67
  - [`BCDD::Result::Context.mixin`](#bcddresultcontextmixin)
67
68
  - [Class example (Instance Methods)](#class-example-instance-methods-1)
68
69
  - [`and_expose`](#and_expose)
@@ -71,7 +72,7 @@ Use it to enable the [Railway Oriented Programming](https://fsharpforfunandprofi
71
72
  - [Mixin add-ons](#mixin-add-ons)
72
73
  - [`BCDD::Result.configuration`](#bcddresultconfiguration)
73
74
  - [`config.addon.enable!(:continue)`](#configaddonenablecontinue)
74
- - [`config.constant_alias.enable!('Result')`](#configconstant_aliasenableresult)
75
+ - [`config.constant_alias.enable!('Result', 'BCDD::Context')`](#configconstant_aliasenableresult-bcddcontext)
75
76
  - [`config.pattern_matching.disable!(:nil_as_valid_value_checking)`](#configpattern_matchingdisablenil_as_valid_value_checking)
76
77
  - [`config.feature.disable!(:expectations)`](#configfeaturedisableexpectations)
77
78
  - [`BCDD::Result.config`](#bcddresultconfig)
@@ -482,7 +483,7 @@ divide(100, 0).value_or { 0 } # 0
482
483
 
483
484
  #### `result.data`
484
485
 
485
- 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`).
486
+ The `BCDD::Result#data` exposes the result attributes (kind, type, value) directly and as a hash (`to_h`/`to_hash`) and array (`to_a`/`to_ary`).
486
487
 
487
488
  This is helpful if you need to access the result attributes generically or want to use Ruby features like splat (`*`) and double splat (`**`) operators.
488
489
 
@@ -491,25 +492,25 @@ See the examples below to understand how to use it.
491
492
  ```ruby
492
493
  result = BCDD::Result::Success(:ok, 1)
493
494
 
494
- success_data = result.data # #<BCDD::Result::Data name=:success type=:ok value=1>
495
+ success_data = result.data # #<BCDD::Result::Data kind=:success type=:ok value=1>
495
496
 
496
- success_data.name # :success
497
+ success_data.kind # :success
497
498
  success_data.type # :ok
498
499
  success_data.value # 1
499
500
 
500
- success_data.to_h # {:name=>:success, :type=>:ok, :value=>1}
501
+ success_data.to_h # {:kind=>:success, :type=>:ok, :value=>1}
501
502
  success_data.to_a # [:success, :ok, 1]
502
503
 
503
- name, type, value = success_data
504
+ kind, type, value = success_data
504
505
 
505
- [name, type, value] # [:success, :ok, 1]
506
+ [kind, type, value] # [:success, :ok, 1]
506
507
 
507
- def print_to_ary(name, type, value)
508
- puts [name, type, value].inspect
508
+ def print_to_ary(kind, type, value)
509
+ puts [kind, type, value].inspect
509
510
  end
510
511
 
511
- def print_to_hash(name:, type:, value:)
512
- puts [name, type, value].inspect
512
+ def print_to_hash(kind:, type:, value:)
513
+ puts [kind, type, value].inspect
513
514
  end
514
515
 
515
516
  print_to_ary(*success_data) # [:success, :ok, 1]
@@ -917,7 +918,9 @@ The `BCDD::Result.mixin` also accepts the `config:` argument. It is a hash that
917
918
 
918
919
  **continue**
919
920
 
920
- This addon will create the `Continue(value)` method, which will know how to produce a `Success(:continued, value)`. It is useful when you want to perform a sequence of operations but want to avoid returning a specific result for each step.
921
+ This addon will create the `Continue(value)` method and change the `Success()` behavior to halt the step chain.
922
+
923
+ 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.
921
924
 
922
925
  ```ruby
923
926
  module Divide
@@ -1350,7 +1353,9 @@ The `BCDD::Result::Expectations.mixin` also accepts the `config:` argument. It i
1350
1353
 
1351
1354
  **Continue**
1352
1355
 
1353
- It is similar to `BCDD::Result.mixin(config: { addon: { continue: true } })`, 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.
1356
+ It is similar to `BCDD::Result.mixin(config: { addon: { continue: true } })`. The key difference is that the expectations will ignore the `Continue(value)`.
1357
+
1358
+ Based on this, use the `Success()` to produce a terminal result and `Continue()` to produce a result that will be used in the next step.
1354
1359
 
1355
1360
  ```ruby
1356
1361
  class Divide
@@ -1435,6 +1440,22 @@ BCDD::Result::Context::Success(:ok, **{ message: 'hashes can be converted to key
1435
1440
 
1436
1441
  <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1437
1442
 
1443
+ #### Constant aliases
1444
+
1445
+ You can configure `Context` or `BCDD::Context` as an alias for `BCDD::Result::Context`. This is helpful to define a standard way to avoid the full constant name/path in your code.
1446
+
1447
+ ```ruby
1448
+ BCDD::Result.configuration do |config|
1449
+ config.context_alias.enable!('BCDD::Context')
1450
+
1451
+ # or
1452
+
1453
+ config.context_alias.enable!('Context')
1454
+ end
1455
+ ```
1456
+
1457
+ <p align="right"><a href="#-bcddresult">⬆️ &nbsp;back to top</a></p>
1458
+
1438
1459
  #### `BCDD::Result::Context.mixin`
1439
1460
 
1440
1461
  As in the `BCDD::Result`, you can use the `BCDD::Result::Context.mixin` to add the `Success()` and `Failure()` methods to your classes/modules.
@@ -1655,7 +1676,9 @@ The `BCDD::Result::Context.mixin` and `BCDD::Result::Context::Expectations.mixin
1655
1676
 
1656
1677
  **Continue**
1657
1678
 
1658
- The `BCDD::Result::Context.mixin(config: { addon: { continue: true } })` or `BCDD::Result::Context::Expectations.mixin(config: { addon: { continue: true } })` adds a `Continue(**input)` that will be ignored by the expectations. This is extremely useful when you want to use `Continue()` to chain operations, but you don't want to declare N success types in the expectations.
1679
+ The `BCDD::Result::Context.mixin(config: { addon: { continue: true } })` or `BCDD::Result::Context::Expectations.mixin(config: { addon: { continue: true } })` creates the `Continue(value)` method and change the `Success()` behavior to halt the step chain.
1680
+
1681
+ 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.
1659
1682
 
1660
1683
  Let's use a mix of `BCDD::Result::Context` features to see in action with this add-on:
1661
1684
 
@@ -1730,11 +1753,11 @@ The `BCDD::Result.configuration` allows you to configure default behaviors for `
1730
1753
  BCDD::Result.configuration do |config|
1731
1754
  config.addon.enable!(:continue)
1732
1755
 
1733
- config.constant_alias.enable!('Result')
1756
+ config.constant_alias.enable!('Result', 'BCDD::Context')
1734
1757
 
1735
1758
  config.pattern_matching.disable!(:nil_as_valid_value_checking)
1736
1759
 
1737
- config.feature.disable!(:expectations) if ::Rails.env.production?
1760
+ # config.feature.disable!(:expectations) if ::Rails.env.production?
1738
1761
  end
1739
1762
  ```
1740
1763
 
@@ -1744,11 +1767,15 @@ Let's see what each configuration in the example above does:
1744
1767
 
1745
1768
  #### `config.addon.enable!(:continue)`
1746
1769
 
1747
- This configuration enables the `Continue()` method for `BCDD::Result` and `BCDD::Result::Context`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).
1770
+ This configuration enables the `Continue()` method for `BCDD::Result`, `BCDD::Result::Context`, `BCDD::Result::Expectation`, and `BCDD::Result::Context::Expectation`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).
1748
1771
 
1749
- #### `config.constant_alias.enable!('Result')`
1772
+ #### `config.constant_alias.enable!('Result', 'BCDD::Context')`
1750
1773
 
1751
- This configuration make `Result` a constant alias for `BCDD::Result`. Link to [documentation](#bcddresult-versus-result).
1774
+ This configuration make `Result` a constant alias for `BCDD::Result`, and `BCDD::Context` a constant alias for `BCDD::Result::Context`.
1775
+
1776
+ Link to documentations:
1777
+ - [Result alias](#bcddresult-versus-result)
1778
+ - [Context aliases](#constant-aliases)
1752
1779
 
1753
1780
  #### `config.pattern_matching.disable!(:nil_as_valid_value_checking)`
1754
1781
 
@@ -1789,15 +1816,14 @@ BCDD::Result.config.addon.options
1789
1816
 
1790
1817
  ```ruby
1791
1818
  BCDD::Result.config.constant_alias.enabled?('Result')
1819
+ BCDD::Result.config.constant_alias.enabled?('Context')
1820
+ BCDD::Result.config.constant_alias.enabled?('BCDD::Context')
1792
1821
 
1793
1822
  BCDD::Result.config.constant_alias.options
1794
1823
  # {
1795
- # "Result"=>{
1796
- # :enabled=>false,
1797
- # :affects=>[
1798
- # "Object"
1799
- # ]
1800
- # }
1824
+ # "Result"=>{:enabled=>false, :affects=>["Object"]},
1825
+ # "Context"=>{:enabled=>false, :affects=>["Object"]},
1826
+ # "BCDD::Context"=>{:enabled=>false, :affects=>["BCDD"]}
1801
1827
  # }
1802
1828
  ```
1803
1829
 
@@ -3,16 +3,18 @@
3
3
  class BCDD::Result
4
4
  class Config
5
5
  module ConstantAlias
6
- RESULT = 'Result'
7
-
8
- OPTIONS = {
9
- RESULT => { default: false, affects: %w[Object] }
10
- }.transform_values!(&:freeze).freeze
11
-
12
6
  MAPPING = {
13
- RESULT => { target: ::Object, name: :Result, value: ::BCDD::Result }
7
+ 'Result' => { target: ::Object, name: :Result, value: ::BCDD::Result },
8
+ 'Context' => { target: ::Object, name: :Context, value: ::BCDD::Result::Context },
9
+ 'BCDD::Context' => { target: ::BCDD, name: :Context, value: ::BCDD::Result::Context }
14
10
  }.transform_values!(&:freeze).freeze
15
11
 
12
+ OPTIONS = MAPPING.to_h do |option_name, mapping|
13
+ affects = mapping.fetch(:target).name.freeze
14
+
15
+ [option_name, { default: false, affects: [affects].freeze }]
16
+ end.freeze
17
+
16
18
  Listener = ->(option_name, boolean) do
17
19
  mapping = MAPPING.fetch(option_name)
18
20
 
@@ -13,13 +13,14 @@ class BCDD::Result
13
13
  default_flags.merge(config_flags).slice(*default_flags.keys)
14
14
  end
15
15
 
16
- def self.filter_map(all_flags, config:, from:)
16
+ def self.select(all_flags, config:, from:)
17
17
  with_defaults(all_flags, config)
18
- .filter_map { |name, truthy| from[name] if truthy }
18
+ .filter_map { |name, truthy| [name, from[name]] if truthy }
19
+ .to_h
19
20
  end
20
21
 
21
22
  def self.addon(map:, from:)
22
- filter_map(map, config: :addon, from: from)
23
+ select(map, config: :addon, from: from)
23
24
  end
24
25
  end
25
26
  end
@@ -4,7 +4,7 @@ class BCDD::Result::Context
4
4
  module Expectations::Mixin
5
5
  Factory = BCDD::Result::Expectations::Mixin::Factory
6
6
 
7
- METHODS = BCDD::Result::Expectations::Mixin::METHODS
7
+ Methods = BCDD::Result::Expectations::Mixin::Methods
8
8
 
9
9
  module Addons
10
10
  module Continuable
@@ -15,11 +15,11 @@ class BCDD::Result::Context
15
15
  private_class_method :mixin!, :mixin_module, :result_factory_without_expectations
16
16
 
17
17
  def Success(type, **value)
18
- Success.new(type: type, value: value, subject: subject, expectations: contract)
18
+ _ResultAs(Success, type, value)
19
19
  end
20
20
 
21
21
  def Failure(type, **value)
22
- Failure.new(type: type, value: value, subject: subject, expectations: contract)
22
+ _ResultAs(Failure, type, value)
23
23
  end
24
24
  end
25
25
  end
@@ -6,18 +6,26 @@ class BCDD::Result::Context
6
6
 
7
7
  module Methods
8
8
  def Success(type, **value)
9
- Success.new(type: type, value: value, subject: self)
9
+ _ResultAs(Success, type, value)
10
10
  end
11
11
 
12
12
  def Failure(type, **value)
13
- Failure.new(type: type, value: value, subject: self)
13
+ _ResultAs(Failure, type, value)
14
+ end
15
+
16
+ private def _ResultAs(kind_class, type, value, halted: nil)
17
+ kind_class.new(type: type, value: value, subject: self, halted: halted)
14
18
  end
15
19
  end
16
20
 
17
21
  module Addons
18
22
  module Continuable
23
+ def Success(type, **value)
24
+ _ResultAs(Success, type, value, halted: true)
25
+ end
26
+
19
27
  private def Continue(**value)
20
- Success.new(type: :continued, value: value, subject: self)
28
+ _ResultAs(Success, :continued, value)
21
29
  end
22
30
  end
23
31
 
@@ -15,7 +15,7 @@ class BCDD::Result
15
15
  Failure.new(type: type, value: value)
16
16
  end
17
17
 
18
- def initialize(type:, value:, subject: nil, expectations: nil)
18
+ def initialize(type:, value:, subject: nil, expectations: nil, halted: nil)
19
19
  value.is_a?(::Hash) or raise ::ArgumentError, 'value must be a Hash'
20
20
 
21
21
  @acc = {}
@@ -35,7 +35,7 @@ class BCDD::Result
35
35
  private
36
36
 
37
37
  def for(data)
38
- case data.name
38
+ case data.kind
39
39
  when :unknown then Contract::Disabled
40
40
  when :success then success
41
41
  else failure
@@ -2,26 +2,26 @@
2
2
 
3
3
  class BCDD::Result
4
4
  class Data
5
- attr_reader :name, :type, :value
5
+ attr_reader :kind, :type, :value
6
6
 
7
- def initialize(name, type, value)
8
- @name = name
7
+ def initialize(kind, type, value)
8
+ @kind = kind
9
9
  @type = type.to_sym
10
10
  @value = value
11
11
  end
12
12
 
13
13
  def to_h
14
- { name: name, type: type, value: value }
14
+ { kind: kind, type: type, value: value }
15
15
  end
16
16
 
17
17
  def to_a
18
- [name, type, value]
18
+ [kind, type, value]
19
19
  end
20
20
 
21
21
  def inspect
22
22
  format(
23
- '#<%<class_name>s name=%<name>p type=%<type>p value=%<value>p>',
24
- class_name: self.class.name, name: name, type: type, value: value
23
+ '#<%<class_name>s kind=%<kind>p type=%<type>p value=%<value>p>',
24
+ class_name: self.class.name, kind: kind, type: type, value: value
25
25
  )
26
26
  end
27
27
 
@@ -11,21 +11,29 @@ class BCDD::Result
11
11
  end
12
12
  end
13
13
 
14
- METHODS = <<~RUBY
15
- def Success(...)
16
- _Result.Success(...)
17
- end
14
+ module Methods
15
+ BASE = <<~RUBY
16
+ def Success(...)
17
+ _Result.Success(...)
18
+ end
18
19
 
19
- def Failure(...)
20
- _Result.Failure(...)
21
- end
20
+ def Failure(...)
21
+ _Result.Failure(...)
22
+ end
23
+ RUBY
22
24
 
23
- private
25
+ FACTORY = <<~RUBY
26
+ private def _Result
27
+ @_Result ||= Result.with(subject: self, halted: %<halted>s)
28
+ end
29
+ RUBY
30
+
31
+ def self.to_eval(addons)
32
+ halted = addons.key?(:continue) ? 'true' : 'nil'
24
33
 
25
- def _Result
26
- @_Result ||= Result.with(subject: self)
34
+ "#{BASE}\n#{format(FACTORY, halted: halted)}"
27
35
  end
28
- RUBY
36
+ end
29
37
 
30
38
  module Addons
31
39
  module Continuable
@@ -15,8 +15,8 @@ class BCDD::Result
15
15
 
16
16
  mod = mixin_module::Factory.module!
17
17
  mod.const_set(:Result, new(success: success, failure: failure, config: config).freeze)
18
- mod.module_eval(mixin_module::METHODS)
19
- mod.send(:include, *addons) unless addons.empty?
18
+ mod.module_eval(mixin_module::Methods.to_eval(addons), __FILE__, __LINE__ + 1)
19
+ mod.send(:include, *addons.values) unless addons.empty?
20
20
  mod
21
21
  end
22
22
 
@@ -38,28 +38,38 @@ class BCDD::Result
38
38
 
39
39
  private_class_method :mixin!, :mixin_module, :result_factory_without_expectations
40
40
 
41
- def initialize(subject: nil, success: nil, failure: nil, contract: nil, config: nil)
41
+ def initialize(subject: nil, contract: nil, halted: nil, **options)
42
+ @halted = halted
43
+
42
44
  @subject = subject
43
45
 
44
46
  @contract = contract if contract.is_a?(Contract::Evaluator)
45
47
 
46
- @contract ||= Contract.new(success: success, failure: failure, config: config).freeze
48
+ @contract ||= Contract.new(
49
+ success: options[:success],
50
+ failure: options[:failure],
51
+ config: options[:config]
52
+ ).freeze
47
53
  end
48
54
 
49
55
  def Success(type, value = nil)
50
- Success.new(type: type, value: value, subject: subject, expectations: contract)
56
+ _ResultAs(Success, type, value)
51
57
  end
52
58
 
53
59
  def Failure(type, value = nil)
54
- Failure.new(type: type, value: value, subject: subject, expectations: contract)
60
+ _ResultAs(Failure, type, value)
55
61
  end
56
62
 
57
- def with(subject:)
58
- self.class.new(subject: subject, contract: contract)
63
+ def with(subject:, halted: nil)
64
+ self.class.new(subject: subject, halted: halted, contract: contract)
59
65
  end
60
66
 
61
67
  private
62
68
 
63
- attr_reader :subject, :contract
69
+ def _ResultAs(kind_class, type, value)
70
+ kind_class.new(type: type, value: value, subject: subject, expectations: contract, halted: halted)
71
+ end
72
+
73
+ attr_reader :subject, :halted, :contract
64
74
  end
65
75
  end
@@ -15,7 +15,7 @@ module BCDD::Result::Failure::Methods
15
15
 
16
16
  private
17
17
 
18
- def name
18
+ def kind
19
19
  :failure
20
20
  end
21
21
  end
@@ -13,18 +13,26 @@ class BCDD::Result
13
13
 
14
14
  module Methods
15
15
  def Success(type, value = nil)
16
- Success.new(type: type, value: value, subject: self)
16
+ _ResultAs(Success, type, value)
17
17
  end
18
18
 
19
19
  def Failure(type, value = nil)
20
- Failure.new(type: type, value: value, subject: self)
20
+ _ResultAs(Failure, type, value)
21
+ end
22
+
23
+ private def _ResultAs(kind_class, type, value, halted: nil)
24
+ kind_class.new(type: type, value: value, subject: self, halted: halted)
21
25
  end
22
26
  end
23
27
 
24
28
  module Addons
25
29
  module Continuable
30
+ def Success(type, value = nil)
31
+ _ResultAs(Success, type, value, halted: true)
32
+ end
33
+
26
34
  private def Continue(value)
27
- Success(:continued, value)
35
+ _ResultAs(Success, :continued, value)
28
36
  end
29
37
  end
30
38
 
@@ -42,7 +50,7 @@ class BCDD::Result
42
50
  mod = mixin_module::Factory.module!
43
51
  mod.send(:include, mixin_module::Methods)
44
52
  mod.const_set(:Result, result_factory)
45
- mod.send(:include, *addons) unless addons.empty?
53
+ mod.send(:include, *addons.values) unless addons.empty?
46
54
  mod
47
55
  end
48
56
 
@@ -15,7 +15,7 @@ module BCDD::Result::Success::Methods
15
15
 
16
16
  private
17
17
 
18
- def name
18
+ def kind
19
19
  :success
20
20
  end
21
21
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module BCDD
4
4
  class Result
5
- VERSION = '0.8.0'
5
+ VERSION = '0.9.0'
6
6
  end
7
7
  end
data/lib/bcdd/result.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require_relative 'result/version'
4
4
  require_relative 'result/error'
5
5
  require_relative 'result/data'
6
- require_relative 'result/config'
7
6
  require_relative 'result/handler'
8
7
  require_relative 'result/failure'
9
8
  require_relative 'result/success'
@@ -11,11 +10,12 @@ require_relative 'result/mixin'
11
10
  require_relative 'result/contract'
12
11
  require_relative 'result/expectations'
13
12
  require_relative 'result/context'
13
+ require_relative 'result/config'
14
14
 
15
15
  class BCDD::Result
16
16
  attr_accessor :unknown
17
17
 
18
- attr_reader :subject, :data, :type_checker
18
+ attr_reader :subject, :data, :type_checker, :halted
19
19
 
20
20
  protected :subject
21
21
 
@@ -31,16 +31,21 @@ class BCDD::Result
31
31
  config.freeze
32
32
  end
33
33
 
34
- def initialize(type:, value:, subject: nil, expectations: nil)
35
- data = Data.new(name, type, value)
34
+ def initialize(type:, value:, subject: nil, expectations: nil, halted: nil)
35
+ data = Data.new(kind, type, value)
36
36
 
37
37
  @type_checker = Contract.evaluate(data, expectations)
38
38
  @subject = subject
39
+ @halted = halted || kind == :failure
39
40
  @data = data
40
41
 
41
42
  self.unknown = true
42
43
  end
43
44
 
45
+ def halted?
46
+ halted
47
+ end
48
+
44
49
  def type
45
50
  data.type
46
51
  end
@@ -80,7 +85,7 @@ class BCDD::Result
80
85
  end
81
86
 
82
87
  def and_then(method_name = nil, context = nil)
83
- return self if failure?
88
+ return self if halted?
84
89
 
85
90
  method_name && block_given? and raise ::ArgumentError, 'method_name and block are mutually exclusive'
86
91
 
@@ -116,7 +121,7 @@ class BCDD::Result
116
121
  end
117
122
 
118
123
  def deconstruct_keys(_keys)
119
- { name => { type => value } }
124
+ { kind => { type => value } }
120
125
  end
121
126
 
122
127
  alias eql? ==
@@ -124,7 +129,7 @@ class BCDD::Result
124
129
 
125
130
  private
126
131
 
127
- def name
132
+ def kind
128
133
  :unknown
129
134
  end
130
135
 
data/sig/bcdd/result.rbs CHANGED
@@ -10,6 +10,7 @@ class BCDD::Result
10
10
 
11
11
  attr_reader data: BCDD::Result::Data
12
12
  attr_reader subject: untyped
13
+ attr_reader halted: bool
13
14
 
14
15
  def self.config: -> BCDD::Result::Config
15
16
  def self.configuration: { (BCDD::Result::Config) -> void } -> BCDD::Result::Config
@@ -18,12 +19,14 @@ class BCDD::Result
18
19
  type: Symbol,
19
20
  value: untyped,
20
21
  ?subject: untyped,
21
- ?expectations: BCDD::Result::Contract::Evaluator
22
+ ?expectations: BCDD::Result::Contract::Evaluator,
23
+ ?halted: bool
22
24
  ) -> void
23
25
 
24
26
  def type: -> Symbol
25
27
  def value: -> untyped
26
28
 
29
+ def halted?: -> bool
27
30
  def success?: (?Symbol type) -> bool
28
31
  def failure?: (?Symbol type) -> bool
29
32
 
@@ -50,7 +53,7 @@ class BCDD::Result
50
53
 
51
54
  private
52
55
 
53
- def name: -> Symbol
56
+ def kind: -> Symbol
54
57
  def known: (Proc) -> untyped
55
58
  def call_subject_method: (Symbol, untyped) -> BCDD::Result
56
59
  def ensure_result_object: (untyped, origin: Symbol) -> BCDD::Result
@@ -65,7 +68,8 @@ class BCDD::Result
65
68
  def value: -> untyped
66
69
 
67
70
  private
68
- def name: -> Symbol
71
+
72
+ def kind: -> Symbol
69
73
  def type_checker: -> BCDD::Result::Contract::TypeChecker
70
74
  end
71
75
 
@@ -85,7 +89,8 @@ class BCDD::Result
85
89
  def value: -> untyped
86
90
 
87
91
  private
88
- def name: -> Symbol
92
+
93
+ def kind: -> Symbol
89
94
  def type_checker: -> BCDD::Result::Contract::TypeChecker
90
95
  end
91
96
 
@@ -105,6 +110,10 @@ class BCDD::Result
105
110
  def Success: (Symbol type, ?untyped value) -> BCDD::Result::Success
106
111
 
107
112
  def Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
113
+
114
+ private
115
+
116
+ def _ResultAs: (singleton(BCDD::Result), Symbol, untyped, ?halted: bool) -> untyped
108
117
  end
109
118
 
110
119
  module Addons
@@ -118,7 +127,7 @@ class BCDD::Result
118
127
 
119
128
  OPTIONS: Hash[Symbol, Module]
120
129
 
121
- def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Array[Module]
130
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
122
131
  end
123
132
  end
124
133
 
@@ -131,7 +140,7 @@ end
131
140
 
132
141
  class BCDD::Result
133
142
  class Data
134
- attr_reader name: Symbol
143
+ attr_reader kind: Symbol
135
144
  attr_reader type: Symbol
136
145
  attr_reader value: untyped
137
146
  attr_reader to_h: Hash[Symbol, untyped]
@@ -365,18 +374,16 @@ class BCDD::Result::Expectations
365
374
 
366
375
  def self.new: (
367
376
  ?subject: untyped,
368
- ?success: Hash[Symbol, untyped] | Array[Symbol],
369
- ?failure: Hash[Symbol, untyped] | Array[Symbol],
370
377
  ?contract: BCDD::Result::Contract::Evaluator,
371
- ?config: Hash[Symbol, Hash[Symbol, bool]]
378
+ ?halted: bool,
379
+ **untyped
372
380
  ) -> (BCDD::Result::Expectations | untyped)
373
381
 
374
382
  def initialize: (
375
383
  ?subject: untyped,
376
- ?success: Hash[Symbol, untyped] | Array[Symbol],
377
- ?failure: Hash[Symbol, untyped] | Array[Symbol],
378
384
  ?contract: BCDD::Result::Contract::Evaluator,
379
- ?config: Hash[Symbol, Hash[Symbol, bool]]
385
+ ?halted: bool,
386
+ **untyped
380
387
  ) -> void
381
388
 
382
389
  def Success: (Symbol, ?untyped) -> BCDD::Result::Success
@@ -386,8 +393,11 @@ class BCDD::Result::Expectations
386
393
 
387
394
  private
388
395
 
396
+ def _ResultAs: (singleton(BCDD::Result), Symbol, untyped) -> untyped
397
+
389
398
  attr_reader subject: untyped
390
399
  attr_reader contract: BCDD::Result::Contract::Evaluator
400
+ attr_reader halted: bool
391
401
  end
392
402
 
393
403
  module BCDD::Result::Expectations::Mixin
@@ -395,7 +405,12 @@ module BCDD::Result::Expectations::Mixin
395
405
  def self.module!: -> Module
396
406
  end
397
407
 
398
- METHODS: String
408
+ module Methods
409
+ BASE: String
410
+ FACTORY: String
411
+
412
+ def self.to_eval: (Hash[Symbol, untyped]) -> String
413
+ end
399
414
 
400
415
  module Addons
401
416
  module Continuable
@@ -404,7 +419,7 @@ module BCDD::Result::Expectations::Mixin
404
419
 
405
420
  OPTIONS: Hash[Symbol, Module]
406
421
 
407
- def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Array[Module]
422
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
408
423
  end
409
424
  end
410
425
 
@@ -419,7 +434,8 @@ class BCDD::Result::Context < BCDD::Result
419
434
  type: Symbol,
420
435
  value: untyped,
421
436
  ?subject: untyped,
422
- ?expectations: BCDD::Result::Contract::Evaluator
437
+ ?expectations: BCDD::Result::Contract::Evaluator,
438
+ ?halted: bool
423
439
  ) -> void
424
440
 
425
441
  def and_then: (?Symbol, **untyped) ?{ (Hash[Symbol, untyped]) -> untyped } -> BCDD::Result::Context
@@ -460,6 +476,10 @@ class BCDD::Result::Context
460
476
  def Success: (Symbol, **untyped) -> BCDD::Result::Context::Success
461
477
 
462
478
  def Failure: (Symbol, **untyped) -> BCDD::Result::Context::Failure
479
+
480
+ private
481
+
482
+ def _ResultAs: (singleton(BCDD::Result::Context), Symbol, untyped, ?halted: bool) -> untyped
463
483
  end
464
484
 
465
485
  module Addons
@@ -473,7 +493,7 @@ class BCDD::Result::Context
473
493
 
474
494
  OPTIONS: Hash[Symbol, Module]
475
495
 
476
- def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Array[Module]
496
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
477
497
  end
478
498
  end
479
499
 
@@ -492,7 +512,7 @@ class BCDD::Result::Context::Expectations < BCDD::Result::Expectations
492
512
  end
493
513
 
494
514
  module BCDD::Result::Context::Expectations::Mixin
495
- METHODS: String
515
+ Methods: singleton(BCDD::Result::Expectations::Mixin::Methods)
496
516
  Factory: singleton(BCDD::Result::Expectations::Mixin::Factory)
497
517
 
498
518
  module Addons
@@ -502,7 +522,7 @@ module BCDD::Result::Context::Expectations::Mixin
502
522
 
503
523
  OPTIONS: Hash[Symbol, Module]
504
524
 
505
- def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Array[Module]
525
+ def self.options: (Hash[Symbol, Hash[Symbol, bool]]) -> Hash[Symbol, Module]
506
526
  end
507
527
  end
508
528
 
@@ -563,10 +583,8 @@ class BCDD::Result::Config::Switcher
563
583
  end
564
584
 
565
585
  module BCDD::Result::Config::ConstantAlias
566
- RESULT: String
567
-
568
- OPTIONS: Hash[String, Hash[Symbol, untyped]]
569
586
  MAPPING: Hash[String, Hash[Symbol, untyped]]
587
+ OPTIONS: Hash[String, Hash[Symbol, untyped]]
570
588
  Listener: Proc
571
589
 
572
590
  def self.switcher: -> BCDD::Result::Config::Switcher
@@ -578,14 +596,14 @@ module BCDD::Result::Config::Options
578
596
  Symbol
579
597
  ) -> Hash[Symbol, bool]
580
598
 
581
- def self.filter_map: (
599
+ def self.select: (
582
600
  Hash[Symbol, Hash[Symbol, bool]],
583
601
  config: Symbol,
584
602
  from: Hash[Symbol, untyped]
585
- ) -> Array[untyped]
603
+ ) -> Hash[Symbol, untyped]
586
604
 
587
605
  def self.addon: (
588
606
  map: Hash[Symbol, Hash[Symbol, bool]],
589
607
  from: Hash[Symbol, Module]
590
- ) -> Array[Module]
608
+ ) -> Hash[Symbol, Module]
591
609
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcdd-result
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.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-12-11 00:00:00.000000000 Z
11
+ date: 2023-12-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Empower Ruby apps with pragmatic use of Result monad, Railway Oriented
14
- Programming, and B/CDD.
13
+ description: Empower Ruby apps with pragmatic use of Result pattern (monad), Railway
14
+ Oriented Programming, and B/CDD.
15
15
  email:
16
16
  - rodrigo.serradura@gmail.com
17
17
  executables: []
@@ -86,6 +86,6 @@ requirements: []
86
86
  rubygems_version: 3.4.19
87
87
  signing_key:
88
88
  specification_version: 4
89
- summary: Empower Ruby apps with pragmatic use of Result monad, Railway Oriented Programming,
90
- and B/CDD.
89
+ summary: Empower Ruby apps with pragmatic use of Result pattern (monad), Railway Oriented
90
+ Programming, and B/CDD.
91
91
  test_files: []