dry-monads 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -73
- data/LICENSE +1 -1
- data/README.md +18 -38
- data/dry-monads.gemspec +32 -30
- data/lib/dry-monads.rb +3 -1
- data/lib/dry/monads.rb +4 -2
- data/lib/dry/monads/all.rb +4 -2
- data/lib/dry/monads/constants.rb +1 -1
- data/lib/dry/monads/conversion_stubs.rb +2 -0
- data/lib/dry/monads/curry.rb +2 -0
- data/lib/dry/monads/do.rb +55 -17
- data/lib/dry/monads/do/all.rb +39 -17
- data/lib/dry/monads/do/mixin.rb +2 -0
- data/lib/dry/monads/either.rb +9 -7
- data/lib/dry/monads/errors.rb +8 -3
- data/lib/dry/monads/lazy.rb +19 -6
- data/lib/dry/monads/list.rb +31 -30
- data/lib/dry/monads/maybe.rb +90 -19
- data/lib/dry/monads/registry.rb +15 -12
- data/lib/dry/monads/result.rb +42 -15
- data/lib/dry/monads/result/fixed.rb +35 -24
- data/lib/dry/monads/right_biased.rb +45 -24
- data/lib/dry/monads/task.rb +25 -22
- data/lib/dry/monads/transformer.rb +4 -1
- data/lib/dry/monads/traverse.rb +9 -1
- data/lib/dry/monads/try.rb +51 -13
- data/lib/dry/monads/unit.rb +6 -2
- data/lib/dry/monads/validated.rb +27 -20
- data/lib/dry/monads/version.rb +3 -1
- data/lib/json/add/dry/monads/maybe.rb +4 -3
- metadata +27 -75
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/ci.yml +0 -74
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -34
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.rubocop.yml +0 -89
- data/.yardopts +0 -4
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -23
- data/Rakefile +0 -6
- data/bin/console +0 -16
- data/bin/setup +0 -7
- data/docsite/source/case-equality.html.md +0 -42
- data/docsite/source/do-notation.html.md +0 -207
- data/docsite/source/getting-started.html.md +0 -142
- data/docsite/source/index.html.md +0 -179
- data/docsite/source/list.html.md +0 -87
- data/docsite/source/maybe.html.md +0 -146
- data/docsite/source/pattern-matching.html.md +0 -68
- data/docsite/source/result.html.md +0 -190
- data/docsite/source/task.html.md +0 -126
- data/docsite/source/tracing-failures.html.md +0 -32
- data/docsite/source/try.html.md +0 -76
- data/docsite/source/unit.html.md +0 -36
- data/docsite/source/validated.html.md +0 -88
- data/log/.gitkeep +0 -0
data/lib/dry/monads/result.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
|
5
|
+
require "dry/monads/constants"
|
6
|
+
require "dry/monads/right_biased"
|
7
|
+
require "dry/monads/transformer"
|
8
|
+
require "dry/monads/conversion_stubs"
|
9
|
+
require "dry/monads/unit"
|
8
10
|
|
9
11
|
module Dry
|
10
12
|
module Monads
|
@@ -84,6 +86,7 @@ module Dry
|
|
84
86
|
|
85
87
|
# @param value [Object] a value of a successful operation
|
86
88
|
def initialize(value)
|
89
|
+
super()
|
87
90
|
@value = value
|
88
91
|
end
|
89
92
|
|
@@ -132,7 +135,7 @@ module Dry
|
|
132
135
|
# @return [String]
|
133
136
|
def to_s
|
134
137
|
if Unit.equal?(@value)
|
135
|
-
|
138
|
+
"Success()"
|
136
139
|
else
|
137
140
|
"Success(#{@value.inspect})"
|
138
141
|
end
|
@@ -145,6 +148,13 @@ module Dry
|
|
145
148
|
def flip
|
146
149
|
Failure.new(@value, RightBiased::Left.trace_caller)
|
147
150
|
end
|
151
|
+
|
152
|
+
# Ignores values and returns self, see {Failure#alt_map}
|
153
|
+
#
|
154
|
+
# @return [Result::Success]
|
155
|
+
def alt_map(_ = nil)
|
156
|
+
self
|
157
|
+
end
|
148
158
|
end
|
149
159
|
|
150
160
|
# Represents a value of a failed operation.
|
@@ -186,6 +196,7 @@ module Dry
|
|
186
196
|
# @param value [Object] failure value
|
187
197
|
# @param trace [String] caller line
|
188
198
|
def initialize(value, trace = RightBiased::Left.trace_caller)
|
199
|
+
super()
|
189
200
|
@value = value
|
190
201
|
@trace = trace
|
191
202
|
end
|
@@ -216,7 +227,8 @@ module Dry
|
|
216
227
|
# otherwise simply returns the first argument.
|
217
228
|
#
|
218
229
|
# @example
|
219
|
-
# Dry::Monads.Failure(ArgumentError.new('error message')).or(&:message)
|
230
|
+
# Dry::Monads.Failure(ArgumentError.new('error message')).or(&:message)
|
231
|
+
# # => "error message"
|
220
232
|
#
|
221
233
|
# @param args [Array<Object>] arguments that will be passed to a block
|
222
234
|
# if one was given, otherwise the first
|
@@ -230,7 +242,8 @@ module Dry
|
|
230
242
|
end
|
231
243
|
end
|
232
244
|
|
233
|
-
# A lifted version of `#or`. Wraps the passed value or the block
|
245
|
+
# A lifted version of `#or`. Wraps the passed value or the block
|
246
|
+
# result with Result::Success.
|
234
247
|
#
|
235
248
|
# @example
|
236
249
|
# Dry::Monads.Failure.new('no value').or_fmap('value') # => Success("value")
|
@@ -245,7 +258,7 @@ module Dry
|
|
245
258
|
# @return [String]
|
246
259
|
def to_s
|
247
260
|
if Unit.equal?(@value)
|
248
|
-
|
261
|
+
"Failure()"
|
249
262
|
else
|
250
263
|
"Failure(#{@value.inspect})"
|
251
264
|
end
|
@@ -285,6 +298,21 @@ module Dry
|
|
285
298
|
def either(_, g)
|
286
299
|
g.(failure)
|
287
300
|
end
|
301
|
+
|
302
|
+
# Lifts a block/proc over Failure
|
303
|
+
#
|
304
|
+
# @overload alt_map(proc)
|
305
|
+
# @param proc [#call]
|
306
|
+
# @return [Result::Failure]
|
307
|
+
#
|
308
|
+
# @overload alt_map
|
309
|
+
# @param block [Proc]
|
310
|
+
# @return [Result::Failure]
|
311
|
+
#
|
312
|
+
def alt_map(proc = Undefined, &block)
|
313
|
+
f = Undefined.default(proc, block)
|
314
|
+
self.class.new(f.(failure), RightBiased::Left.trace_caller)
|
315
|
+
end
|
288
316
|
end
|
289
317
|
|
290
318
|
# A module that can be included for easier access to Result monads.
|
@@ -299,7 +327,6 @@ module Dry
|
|
299
327
|
# Value constructors
|
300
328
|
#
|
301
329
|
module Constructors
|
302
|
-
|
303
330
|
# Success constructor
|
304
331
|
#
|
305
332
|
# @overload Success(value)
|
@@ -386,7 +413,7 @@ module Dry
|
|
386
413
|
# @param fail [#call] Fallback value
|
387
414
|
# @param block [Proc] Fallback block
|
388
415
|
# @return [Success<Any>]
|
389
|
-
def to_result(
|
416
|
+
def to_result(_fail = Unit)
|
390
417
|
Result::Success.new(@value)
|
391
418
|
end
|
392
419
|
end
|
@@ -397,7 +424,7 @@ module Dry
|
|
397
424
|
# @param fail [#call] Fallback value
|
398
425
|
# @param block [Proc] Fallback block
|
399
426
|
# @return [Failure<Any>]
|
400
|
-
def to_result(fail = Unit
|
427
|
+
def to_result(fail = Unit)
|
401
428
|
if block_given?
|
402
429
|
Result::Failure.new(yield)
|
403
430
|
else
|
@@ -447,7 +474,7 @@ module Dry
|
|
447
474
|
end
|
448
475
|
|
449
476
|
class Invalid < Validated
|
450
|
-
#
|
477
|
+
# Converts to Result::Failure
|
451
478
|
#
|
452
479
|
# @return [Result::Failure]
|
453
480
|
def to_result
|
@@ -456,7 +483,7 @@ module Dry
|
|
456
483
|
end
|
457
484
|
end
|
458
485
|
|
459
|
-
require
|
486
|
+
require "dry/monads/registry"
|
460
487
|
register_mixin(:result, Result::Mixin)
|
461
488
|
end
|
462
489
|
end
|
@@ -1,35 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads/constants"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Monads
|
7
|
+
class Result
|
8
|
+
# @see Monads#Result
|
9
|
+
# @private
|
10
|
+
class Fixed < ::Module
|
11
|
+
def self.[](error, **options)
|
12
|
+
new(error, **options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(error, **_options)
|
16
|
+
super()
|
9
17
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
@mod = ::Module.new do
|
19
|
+
define_method(:Failure) do |value|
|
20
|
+
if error === value
|
21
|
+
Failure.new(value, RightBiased::Left.trace_caller)
|
22
|
+
else
|
23
|
+
# rubocop:disable Style/RaiseArgs
|
24
|
+
# per https://github.com/dry-rb/dry-monads/pull/142
|
25
|
+
raise InvalidFailureTypeError.new(value)
|
26
|
+
# rubocop:enable Style/RaiseArgs
|
27
|
+
end
|
17
28
|
end
|
18
|
-
end
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
|
30
|
+
def Success(value = Undefined, &block)
|
31
|
+
v = Undefined.default(value, block || Unit)
|
32
|
+
Success.new(v)
|
33
|
+
end
|
23
34
|
end
|
24
35
|
end
|
25
|
-
end
|
26
36
|
|
27
|
-
|
37
|
+
private
|
28
38
|
|
29
|
-
|
30
|
-
|
39
|
+
def included(base)
|
40
|
+
super
|
31
41
|
|
32
|
-
|
42
|
+
base.include(@mod)
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads/constants"
|
4
|
+
require "dry/monads/unit"
|
5
|
+
require "dry/monads/curry"
|
6
|
+
require "dry/monads/errors"
|
5
7
|
|
6
8
|
module Dry
|
7
9
|
module Monads
|
@@ -10,7 +12,7 @@ module Dry
|
|
10
12
|
# Right part
|
11
13
|
#
|
12
14
|
# @api public
|
13
|
-
module Right
|
15
|
+
module Right # rubocop:disable Metrics/ModuleLength
|
14
16
|
# @private
|
15
17
|
def self.included(m)
|
16
18
|
super
|
@@ -88,6 +90,16 @@ module Dry
|
|
88
90
|
self
|
89
91
|
end
|
90
92
|
|
93
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
94
|
+
# identical to that of {RightBiased::Left}.
|
95
|
+
#
|
96
|
+
# @param _alt [RightBiased::Right, RightBiased::Left]
|
97
|
+
#
|
98
|
+
# @return [RightBiased::Right]
|
99
|
+
def |(_alt)
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
91
103
|
# A lifted version of `#or`. For {RightBiased::Right} acts in the same way as `#or`,
|
92
104
|
# that is returns itselt.
|
93
105
|
#
|
@@ -116,18 +128,18 @@ module Dry
|
|
116
128
|
# create_user.apply(name) # => Failure(:name_missing)
|
117
129
|
#
|
118
130
|
# @return [RightBiased::Left,RightBiased::Right]
|
119
|
-
def apply(val = Undefined)
|
131
|
+
def apply(val = Undefined, &block)
|
120
132
|
unless @value.respond_to?(:call)
|
121
|
-
raise TypeError, "Cannot apply #{
|
133
|
+
raise TypeError, "Cannot apply #{val.inspect} to #{@value.inspect}"
|
122
134
|
end
|
123
135
|
|
124
|
-
Undefined.default(val)
|
136
|
+
Undefined.default(val, &block).fmap { |unwrapped| curry.(unwrapped) }
|
125
137
|
end
|
126
138
|
|
127
139
|
# @param other [Object]
|
128
140
|
# @return [Boolean]
|
129
141
|
def ===(other)
|
130
|
-
self.class
|
142
|
+
other.instance_of?(self.class) && value! === other.value!
|
131
143
|
end
|
132
144
|
|
133
145
|
# Maps the value to Dry::Monads::Unit, useful when you don't care
|
@@ -195,11 +207,11 @@ module Dry
|
|
195
207
|
# @api private
|
196
208
|
def deconstruct
|
197
209
|
if Unit.equal?(@value)
|
198
|
-
|
199
|
-
elsif
|
200
|
-
@value
|
201
|
-
else
|
210
|
+
EMPTY_ARRAY
|
211
|
+
elsif !@value.is_a?(::Array)
|
202
212
|
[@value]
|
213
|
+
else
|
214
|
+
@value
|
203
215
|
end
|
204
216
|
end
|
205
217
|
|
@@ -214,9 +226,9 @@ module Dry
|
|
214
226
|
# end
|
215
227
|
#
|
216
228
|
# @api private
|
217
|
-
def deconstruct_keys(
|
218
|
-
if @value.
|
219
|
-
@value
|
229
|
+
def deconstruct_keys(keys)
|
230
|
+
if @value.respond_to?(:deconstruct_keys)
|
231
|
+
@value.deconstruct_keys(keys)
|
220
232
|
else
|
221
233
|
EMPTY_HASH
|
222
234
|
end
|
@@ -224,7 +236,7 @@ module Dry
|
|
224
236
|
|
225
237
|
private
|
226
238
|
|
227
|
-
if RUBY_VERSION >=
|
239
|
+
if RUBY_VERSION >= "2.7"
|
228
240
|
# @api private
|
229
241
|
def destructure(value)
|
230
242
|
if value.is_a?(::Hash)
|
@@ -253,12 +265,12 @@ module Dry
|
|
253
265
|
# @private
|
254
266
|
# @return [String] Caller location
|
255
267
|
def self.trace_caller
|
256
|
-
caller_locations(2,
|
268
|
+
caller_locations(2, 1)[0].to_s
|
257
269
|
end
|
258
270
|
|
259
271
|
# Raises an error on accessing internal value
|
260
272
|
def value!
|
261
|
-
raise UnwrapError
|
273
|
+
raise UnwrapError, self
|
262
274
|
end
|
263
275
|
|
264
276
|
# Ignores the input parameter and returns self. It exists to keep the interface
|
@@ -297,11 +309,20 @@ module Dry
|
|
297
309
|
raise NotImplementedError
|
298
310
|
end
|
299
311
|
|
312
|
+
# Returns the passed value. Works in pair with {RightBiased::Right#|}.
|
313
|
+
#
|
314
|
+
# @param alt [RightBiased::Right, RightBiased::Left]
|
315
|
+
#
|
316
|
+
# @return [RightBiased::Right, RightBiased::Left]
|
317
|
+
def |(alt)
|
318
|
+
self.or(alt)
|
319
|
+
end
|
320
|
+
|
300
321
|
# A lifted version of `#or`. This is basically `#or` + `#fmap`.
|
301
322
|
#
|
302
323
|
# @example
|
303
|
-
# Dry::Monads.None.
|
304
|
-
# Dry::Monads.None.
|
324
|
+
# Dry::Monads.None.or_fmap('no value') # => Some("no value")
|
325
|
+
# Dry::Monads.None.or_fmap { Time.now } # => Some(current time)
|
305
326
|
#
|
306
327
|
# @return [RightBiased::Left, RightBiased::Right]
|
307
328
|
def or_fmap(*)
|
@@ -381,9 +402,9 @@ module Dry
|
|
381
402
|
# end
|
382
403
|
#
|
383
404
|
# @api private
|
384
|
-
def deconstruct_keys(
|
385
|
-
if @value.
|
386
|
-
@value
|
405
|
+
def deconstruct_keys(keys)
|
406
|
+
if @value.respond_to?(:deconstruct_keys)
|
407
|
+
@value.deconstruct_keys(keys)
|
387
408
|
else
|
388
409
|
EMPTY_HASH
|
389
410
|
end
|
data/lib/dry/monads/task.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
3
|
+
require "concurrent/promise"
|
4
|
+
|
5
|
+
require "dry/monads/unit"
|
6
|
+
require "dry/monads/curry"
|
7
|
+
require "dry/monads/conversion_stubs"
|
6
8
|
|
7
9
|
module Dry
|
8
10
|
module Monads
|
@@ -46,9 +48,11 @@ module Dry
|
|
46
48
|
# @example using a predefined executor
|
47
49
|
# Task[:fast] { do_quick_task }
|
48
50
|
#
|
49
|
-
# @param executor [Concurrent::AbstractExecutorService,Symbol]
|
50
|
-
#
|
51
|
-
#
|
51
|
+
# @param executor [Concurrent::AbstractExecutorService,Symbol]
|
52
|
+
# Either an executor instance
|
53
|
+
# or a name of predefined global
|
54
|
+
# from concurrent-ruby
|
55
|
+
#
|
52
56
|
# @return [Task]
|
53
57
|
def [](executor, &block)
|
54
58
|
new(Promise.execute(executor: executor, &block))
|
@@ -129,14 +133,14 @@ module Dry
|
|
129
133
|
state = case promise.state
|
130
134
|
when :fulfilled
|
131
135
|
if Unit.equal?(value!)
|
132
|
-
|
136
|
+
"value=()"
|
133
137
|
else
|
134
138
|
"value=#{value!.inspect}"
|
135
139
|
end
|
136
140
|
when :rejected
|
137
|
-
"error=#{
|
141
|
+
"error=#{promise.reason.inspect}"
|
138
142
|
else
|
139
|
-
|
143
|
+
"?"
|
140
144
|
end
|
141
145
|
|
142
146
|
"Task(#{state})"
|
@@ -162,16 +166,14 @@ module Dry
|
|
162
166
|
)
|
163
167
|
|
164
168
|
promise.on_error do |v|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
child.on_reject(e)
|
172
|
-
end
|
169
|
+
inner = block.(v).promise
|
170
|
+
inner.execute
|
171
|
+
inner.on_success { |r| child.on_fulfill(r) }
|
172
|
+
inner.on_error { |e| child.on_reject(e) }
|
173
|
+
rescue StandardError => e
|
174
|
+
child.on_reject(e)
|
173
175
|
end
|
174
|
-
promise.on_success
|
176
|
+
promise.on_success { |v| child.on_fulfill(v) }
|
175
177
|
|
176
178
|
self.class.new(child)
|
177
179
|
end
|
@@ -200,6 +202,7 @@ module Dry
|
|
200
202
|
def ==(other)
|
201
203
|
return true if equal?(other)
|
202
204
|
return false unless self.class == other.class
|
205
|
+
|
203
206
|
compare_promises(promise, other.promise)
|
204
207
|
end
|
205
208
|
|
@@ -233,8 +236,8 @@ module Dry
|
|
233
236
|
#
|
234
237
|
# @param val [Task]
|
235
238
|
# @return [Task]
|
236
|
-
def apply(val = Undefined)
|
237
|
-
arg = Undefined.default(val)
|
239
|
+
def apply(val = Undefined, &block)
|
240
|
+
arg = Undefined.default(val, &block)
|
238
241
|
bind { |f| arg.fmap { |v| curry(f).(v) } }
|
239
242
|
end
|
240
243
|
|
@@ -311,7 +314,7 @@ module Dry
|
|
311
314
|
end
|
312
315
|
end
|
313
316
|
|
314
|
-
require
|
317
|
+
require "dry/monads/registry"
|
315
318
|
register_mixin(:task, Task::Mixin)
|
316
319
|
end
|
317
320
|
end
|