bcdd-result 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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