bcdd-result 0.3.0 → 0.5.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.
data/lib/bcdd/result.rb CHANGED
@@ -2,26 +2,38 @@
2
2
 
3
3
  require_relative 'result/version'
4
4
  require_relative 'result/error'
5
- require_relative 'result/type'
5
+ require_relative 'result/data'
6
6
  require_relative 'result/handler'
7
7
  require_relative 'result/failure'
8
8
  require_relative 'result/success'
9
-
10
- require_relative 'resultable'
9
+ require_relative 'result/mixin'
10
+ require_relative 'result/expectations'
11
11
 
12
12
  class BCDD::Result
13
- attr_reader :_type, :value, :subject
13
+ attr_accessor :unknown
14
+
15
+ attr_reader :subject, :data, :type_checker
14
16
 
15
17
  protected :subject
16
18
 
17
- def initialize(type:, value:, subject: nil)
18
- @_type = Type.new(type)
19
- @value = value
19
+ private :unknown, :unknown=, :type_checker
20
+
21
+ def initialize(type:, value:, subject: nil, expectations: nil)
22
+ data = Data.new(name, type, value)
23
+
24
+ @type_checker = Expectations.evaluate(data, expectations)
20
25
  @subject = subject
26
+ @data = data
27
+
28
+ self.unknown = true
21
29
  end
22
30
 
23
31
  def type
24
- _type.to_sym
32
+ data.type
33
+ end
34
+
35
+ def value
36
+ data.value
25
37
  end
26
38
 
27
39
  def success?(_type = nil)
@@ -36,31 +48,22 @@ class BCDD::Result
36
48
  raise Error::NotImplemented
37
49
  end
38
50
 
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
51
+ def on(*types, &block)
52
+ raise Error::MissingTypeArgument if types.empty?
47
53
 
48
- def inspect
49
- format('#<%<class_name>s type=%<type>p value=%<value>p>', class_name: self.class.name, type: type, value: value)
54
+ tap { known(block) if type_checker.allow?(types) }
50
55
  end
51
56
 
52
- def on(*types)
53
- raise Error::MissingTypeArgument if types.empty?
54
-
55
- tap { yield(value, type) if _type.in?(types, allow_empty: false) }
57
+ def on_success(*types, &block)
58
+ tap { known(block) if type_checker.allow_success?(types) && success? }
56
59
  end
57
60
 
58
- def on_success(*types)
59
- tap { yield(value, type) if success? && _type.in?(types, allow_empty: true) }
61
+ def on_failure(*types, &block)
62
+ tap { known(block) if type_checker.allow_failure?(types) && failure? }
60
63
  end
61
64
 
62
- def on_failure(*types)
63
- tap { yield(value, type) if failure? && _type.in?(types, allow_empty: true) }
65
+ def on_unknown
66
+ tap { yield(value, type) if unknown }
64
67
  end
65
68
 
66
69
  def and_then(method_name = nil)
@@ -74,19 +77,48 @@ class BCDD::Result
74
77
  end
75
78
 
76
79
  def handle
77
- handler = Handler.new(self)
80
+ handler = Handler.new(self, type_checker: type_checker)
78
81
 
79
82
  yield handler
80
83
 
81
84
  handler.send(:outcome)
82
85
  end
83
86
 
84
- alias data value
85
- alias data_or value_or
87
+ def ==(other)
88
+ self.class == other.class && type == other.type && value == other.value
89
+ end
90
+
91
+ def hash
92
+ [self.class, type, value].hash
93
+ end
94
+
95
+ def inspect
96
+ format('#<%<class_name>s type=%<type>p value=%<value>p>', class_name: self.class.name, type: type, value: value)
97
+ end
98
+
99
+ def deconstruct
100
+ [type, value]
101
+ end
102
+
103
+ def deconstruct_keys(_keys)
104
+ { name => { type => value } }
105
+ end
106
+
107
+ alias eql? ==
86
108
  alias on_type on
87
109
 
88
110
  private
89
111
 
112
+ def name
113
+ :unknown
114
+ end
115
+
116
+ def known(block)
117
+ self.unknown = false
118
+
119
+ block.call(value, type)
120
+ end
121
+
90
122
  def call_subject_method(method_name)
91
123
  method = subject.method(method_name)
92
124
 
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,40 +5,50 @@ module BCDD
5
5
  end
6
6
 
7
7
  class BCDD::Result
8
- attr_reader _type: BCDD::Result::Type
9
- attr_reader value: untyped
8
+ private attr_accessor unknown: bool
9
+ private attr_reader type_checker: BCDD::Result::Expectations::TypeChecker
10
+
11
+ attr_reader data: BCDD::Result::Data
10
12
  attr_reader subject: untyped
11
13
 
12
- def initialize: (type: Symbol, value: untyped, ?subject: untyped) -> void
14
+ def initialize: (
15
+ type: Symbol,
16
+ value: untyped,
17
+ ?subject: untyped,
18
+ ?expectations: BCDD::Result::Expectations::Contract::Evaluator
19
+ ) -> void
13
20
 
14
21
  def type: -> Symbol
22
+ def value: -> untyped
15
23
 
16
24
  def success?: (?Symbol type) -> bool
17
25
  def failure?: (?Symbol type) -> bool
18
26
 
19
27
  def value_or: { () -> untyped } -> untyped
20
28
 
21
- def ==: (untyped) -> bool
22
- alias eql? ==
23
-
24
- def hash: -> Integer
25
-
26
- def inspect: -> String
27
-
28
29
  def on: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
29
30
  def on_success: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
30
31
  def on_failure: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
32
+ def on_unknown: () { (untyped, Symbol) -> void } -> BCDD::Result
31
33
 
32
34
  def and_then: (?Symbol method_name) { (untyped) -> untyped } -> BCDD::Result
33
35
 
34
- def handle: { (BCDD::Result::Handler) -> void } -> untyped
36
+ def handle: () { (BCDD::Result::Handler) -> void } -> untyped
35
37
 
36
- alias data value
37
- alias data_or value_or
38
+ def ==: (untyped) -> bool
39
+ def hash: -> Integer
40
+ def inspect: -> String
41
+
42
+ def deconstruct: -> [Symbol, [Symbol, untyped]]
43
+ def deconstruct_keys: (Array[Symbol]) -> Hash[Symbol, Hash[Symbol, untyped]]
44
+
45
+ alias eql? ==
38
46
  alias on_type on
39
47
 
40
48
  private
41
49
 
50
+ def name: -> Symbol
51
+ def known: (Proc) -> untyped
42
52
  def call_subject_method: (Symbol) -> BCDD::Result
43
53
  def ensure_result_object: (untyped, origin: Symbol) -> BCDD::Result
44
54
  end
@@ -58,32 +68,7 @@ class BCDD::Result
58
68
  end
59
69
 
60
70
  class BCDD::Result
61
- class Handler
62
- UNDEFINED: Object
63
-
64
- def initialize: (BCDD::Result) -> void
65
-
66
- def []: (*Symbol) { (untyped, Symbol) -> void } -> untyped
67
- def failure: (*Symbol) { (untyped, Symbol) -> void } -> untyped
68
- def success: (*Symbol) { (untyped, Symbol) -> void } -> untyped
69
-
70
- alias type []
71
-
72
- private
73
-
74
- attr_reader _type: BCDD::Result::Type
75
- attr_reader result: BCDD::Result
76
-
77
- def outcome?: -> bool
78
-
79
- def outcome: -> untyped
80
-
81
- def outcome=: (Proc) -> void
82
- end
83
- end
84
-
85
- module BCDD
86
- module Resultable
71
+ module Mixin
87
72
  def Success: (Symbol type, ?untyped value) -> BCDD::Result::Success
88
73
 
89
74
  def Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
@@ -91,12 +76,19 @@ module BCDD
91
76
  end
92
77
 
93
78
  class BCDD::Result
94
- class Type
95
- attr_reader to_sym: Symbol
79
+ class Data
80
+ attr_reader name: Symbol
81
+ attr_reader type: Symbol
82
+ attr_reader value: untyped
83
+ attr_reader to_h: Hash[Symbol, untyped]
84
+ attr_reader to_a: [Symbol, Symbol, untyped]
85
+
86
+ def initialize: (Symbol, Symbol, untyped) -> void
96
87
 
97
- def initialize: (Symbol) -> void
88
+ def inspect: -> String
98
89
 
99
- def in?: (Array[Symbol], allow_empty: bool) -> bool
90
+ alias to_ary to_a
91
+ alias to_hash to_h
100
92
  end
101
93
  end
102
94
 
@@ -124,5 +116,194 @@ class BCDD::Result
124
116
  def self.build: (subject: untyped, method: ::Method)
125
117
  -> BCDD::Result::Error::WrongSubjectMethodArity
126
118
  end
119
+
120
+ class UnhandledTypes < BCDD::Result::Error
121
+ def self.build: (types: Set[Symbol])
122
+ -> BCDD::Result::Error::UnhandledTypes
123
+ end
124
+ end
125
+ end
126
+
127
+ class BCDD::Result
128
+ class Handler
129
+ UNDEFINED: Object
130
+
131
+ def initialize: (
132
+ BCDD::Result,
133
+ type_checker: BCDD::Result::Expectations::TypeChecker
134
+ ) -> void
135
+
136
+ def []: (*Symbol) { (untyped, Symbol) -> void } -> untyped
137
+ def failure: (*Symbol) { (untyped, Symbol) -> void } -> untyped
138
+ def success: (*Symbol) { (untyped, Symbol) -> void } -> untyped
139
+ def unknown: () { (untyped, Symbol) -> void } -> untyped
140
+
141
+ alias type []
142
+
143
+ private
144
+
145
+ attr_reader result: BCDD::Result
146
+ attr_reader allowed_types: BCDD::Result::Handler::AllowedTypes
147
+
148
+ def outcome?: -> bool
149
+ def outcome=: (Proc) -> void
150
+ def outcome: -> untyped
151
+ end
152
+ end
153
+
154
+ class BCDD::Result::Handler
155
+ class AllowedTypes
156
+ attr_reader unchecked: Set[Symbol]
157
+ attr_reader type_checker: BCDD::Result::Expectations::TypeChecker
158
+
159
+ def initialize: (
160
+ BCDD::Result::Expectations::TypeChecker
161
+ ) -> void
162
+
163
+ def allow?: (Array[Symbol]) -> bool
164
+ def allow_success?: (Array[Symbol]) -> bool
165
+ def allow_failure?: (Array[Symbol]) -> bool
166
+
167
+ def all_checked?: -> bool
168
+
169
+ private
170
+
171
+ def check!: (Array[Symbol], bool) -> bool
172
+ end
173
+ end
174
+
175
+ class BCDD::Result::Expectations
176
+ MIXIN_METHODS: String
177
+
178
+ def self.mixin: (
179
+ ?success: Hash[Symbol, untyped] | Array[Symbol],
180
+ ?failure: Hash[Symbol, untyped] | Array[Symbol]
181
+ ) -> Module
182
+
183
+ def self.evaluate: (
184
+ BCDD::Result::Data,
185
+ BCDD::Result::Expectations::Contract::Evaluator
186
+ ) -> BCDD::Result::Expectations::TypeChecker
187
+
188
+ def initialize: (
189
+ ?subject: untyped,
190
+ ?success: Hash[Symbol, untyped] | Array[Symbol],
191
+ ?failure: Hash[Symbol, untyped] | Array[Symbol],
192
+ ?contract: BCDD::Result::Expectations::Contract::Evaluator
193
+ ) -> void
194
+
195
+ def Success: (Symbol, ?untyped) -> BCDD::Result::Success
196
+ def Failure: (Symbol, ?untyped) -> BCDD::Result::Failure
197
+
198
+ private
199
+
200
+ attr_reader subject: untyped
201
+ attr_reader contract: BCDD::Result::Expectations::Contract::Evaluator
202
+ end
203
+
204
+ module BCDD::Result::Expectations::Contract
205
+ NONE: BCDD::Result::Expectations::Contract::Evaluator
206
+
207
+ ToEnsure: ^(Hash[Symbol, untyped] | Array[Symbol])
208
+ -> BCDD::Result::Expectations::Contract::Evaluator
209
+
210
+ def self.new: (
211
+ success: Hash[Symbol, untyped] | Array[Symbol],
212
+ failure: Hash[Symbol, untyped] | Array[Symbol]
213
+ ) -> BCDD::Result::Expectations::Contract::Evaluator
214
+ end
215
+
216
+ class BCDD::Result::Expectations
217
+ class TypeChecker
218
+ attr_reader result_type: Symbol
219
+ attr_reader expectations: BCDD::Result::Expectations::Contract::Evaluator
220
+
221
+ def initialize: (
222
+ Symbol,
223
+ expectations: BCDD::Result::Expectations::Contract::Evaluator
224
+ ) -> void
225
+
226
+ def allow?: (Array[Symbol]) -> bool
227
+ def allow_success?: (Array[Symbol]) -> bool
228
+ def allow_failure?: (Array[Symbol]) -> bool
229
+
230
+ private
231
+
232
+ def validate: (
233
+ Array[Symbol],
234
+ expected: BCDD::Result::Expectations::Contract::Interface,
235
+ allow_empty: bool
236
+ ) -> bool
237
+ end
238
+ end
239
+
240
+ class BCDD::Result::Expectations::Error < BCDD::Result::Error
241
+ class UnexpectedType < BCDD::Result::Expectations::Error
242
+ def self.build: (type: Symbol, allowed_types: Set[Symbol])
243
+ -> BCDD::Result::Expectations::Error::UnexpectedType
244
+ end
245
+
246
+ class UnexpectedValue < BCDD::Result::Expectations::Error
247
+ def self.build: (type: Symbol, value: untyped)
248
+ -> BCDD::Result::Expectations::Error::UnexpectedValue
249
+ end
250
+ end
251
+
252
+ module BCDD::Result::Expectations::Contract
253
+ module Interface
254
+ def ==: (BCDD::Result::Expectations::Contract::Interface) -> bool
255
+
256
+ def allowed_types: -> Set[Symbol]
257
+
258
+ def type?: (Symbol) -> bool
259
+
260
+ def type!: (Symbol) -> Symbol
261
+
262
+ def type_and_value!: (BCDD::Result::Data) -> void
263
+
264
+ def !=: (untyped) -> bool
265
+ end
266
+ end
267
+
268
+ module BCDD::Result::Expectations::Contract
269
+ module Disabled
270
+ extend Interface
271
+
272
+ EMPTY_SET: Set[Symbol]
273
+ end
274
+ end
275
+
276
+ module BCDD::Result::Expectations::Contract
277
+ class ForTypes
278
+ include Interface
279
+
280
+ def initialize: (Array[Symbol]) -> void
281
+ end
282
+ end
283
+
284
+ module BCDD::Result::Expectations::Contract
285
+ class ForTypesAndValues
286
+ include Interface
287
+
288
+ def initialize: (Hash[Symbol, untyped]) -> void
289
+ end
290
+ end
291
+
292
+ module BCDD::Result::Expectations::Contract
293
+ class Evaluator
294
+ include Interface
295
+
296
+ attr_reader allowed_types: Set[Symbol]
297
+ attr_reader success: BCDD::Result::Expectations::Contract::Interface
298
+ attr_reader failure: BCDD::Result::Expectations::Contract::Interface
299
+
300
+ def initialize: (
301
+ BCDD::Result::Expectations::Contract::Interface,
302
+ BCDD::Result::Expectations::Contract::Interface
303
+ ) -> void
304
+
305
+ private
306
+
307
+ def for: (BCDD::Result::Data) -> BCDD::Result::Expectations::Contract::Interface
127
308
  end
128
309
  end
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.5.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-10-10 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,24 @@ 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
34
+ - lib/bcdd/result/expectations.rb
35
+ - lib/bcdd/result/expectations/contract.rb
36
+ - lib/bcdd/result/expectations/contract/disabled.rb
37
+ - lib/bcdd/result/expectations/contract/evaluator.rb
38
+ - lib/bcdd/result/expectations/contract/for_types.rb
39
+ - lib/bcdd/result/expectations/contract/for_types_and_values.rb
40
+ - lib/bcdd/result/expectations/contract/interface.rb
41
+ - lib/bcdd/result/expectations/error.rb
42
+ - lib/bcdd/result/expectations/type_checker.rb
31
43
  - lib/bcdd/result/failure.rb
32
44
  - lib/bcdd/result/handler.rb
45
+ - lib/bcdd/result/handler/allowed_types.rb
46
+ - lib/bcdd/result/mixin.rb
33
47
  - lib/bcdd/result/success.rb
34
- - lib/bcdd/result/type.rb
35
48
  - lib/bcdd/result/version.rb
36
- - lib/bcdd/resultable.rb
49
+ - lib/result.rb
37
50
  - sig/bcdd/result.rbs
38
51
  homepage: https://github.com/b-cdd/result
39
52
  licenses:
@@ -62,5 +75,5 @@ requirements: []
62
75
  rubygems_version: 3.4.19
63
76
  signing_key:
64
77
  specification_version: 4
65
- summary: A result abstraction (monad based) for Ruby.
78
+ summary: A pragmatic result abstraction (monad based) for Ruby.
66
79
  test_files: []
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class BCDD::Result
4
- class Type
5
- attr_reader :to_sym
6
-
7
- def initialize(type)
8
- @to_sym = type.to_sym
9
- end
10
-
11
- def in?(types, allow_empty: false)
12
- (allow_empty && types.empty?) || types.any?(to_sym)
13
- end
14
- end
15
-
16
- private_constant :Type
17
- end
@@ -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