dry-monads 1.3.5 → 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 +140 -80
- data/LICENSE +1 -1
- data/README.md +5 -4
- data/dry-monads.gemspec +30 -30
- data/lib/dry-monads.rb +1 -1
- data/lib/dry/monads.rb +2 -2
- data/lib/dry/monads/all.rb +2 -2
- data/lib/dry/monads/constants.rb +1 -1
- data/lib/dry/monads/do.rb +52 -18
- data/lib/dry/monads/do/all.rb +36 -17
- data/lib/dry/monads/either.rb +7 -7
- data/lib/dry/monads/errors.rb +5 -2
- data/lib/dry/monads/lazy.rb +15 -4
- data/lib/dry/monads/list.rb +28 -28
- data/lib/dry/monads/maybe.rb +87 -19
- data/lib/dry/monads/registry.rb +10 -10
- data/lib/dry/monads/result.rb +38 -12
- data/lib/dry/monads/result/fixed.rb +33 -24
- data/lib/dry/monads/right_biased.rb +35 -16
- data/lib/dry/monads/task.rb +20 -20
- data/lib/dry/monads/transformer.rb +2 -1
- data/lib/dry/monads/traverse.rb +7 -1
- data/lib/dry/monads/try.rb +45 -12
- data/lib/dry/monads/unit.rb +6 -2
- data/lib/dry/monads/validated.rb +20 -16
- data/lib/dry/monads/version.rb +1 -1
- data/lib/json/add/dry/monads/maybe.rb +3 -3
- metadata +18 -69
- 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 -30
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/ci.yml +0 -52
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -56
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.rubocop.yml +0 -101
- data/.yardopts +0 -4
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -19
- data/Gemfile.devtools +0 -14
- data/Rakefile +0 -8
- data/bin/.gitkeep +0 -0
- data/bin/console +0 -17
- 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/project.yml +0 -2
@@ -1,37 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class
|
8
|
-
|
9
|
-
|
10
|
-
|
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()
|
11
17
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
19
28
|
end
|
20
|
-
end
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
|
30
|
+
def Success(value = Undefined, &block)
|
31
|
+
v = Undefined.default(value, block || Unit)
|
32
|
+
Success.new(v)
|
33
|
+
end
|
25
34
|
end
|
26
35
|
end
|
27
|
-
end
|
28
36
|
|
29
|
-
|
37
|
+
private
|
30
38
|
|
31
|
-
|
32
|
-
|
39
|
+
def included(base)
|
40
|
+
super
|
33
41
|
|
34
|
-
|
42
|
+
base.include(@mod)
|
43
|
+
end
|
35
44
|
end
|
36
45
|
end
|
37
46
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "dry/monads/constants"
|
4
|
+
require "dry/monads/unit"
|
5
|
+
require "dry/monads/curry"
|
6
|
+
require "dry/monads/errors"
|
7
7
|
|
8
8
|
module Dry
|
9
9
|
module Monads
|
@@ -12,7 +12,7 @@ module Dry
|
|
12
12
|
# Right part
|
13
13
|
#
|
14
14
|
# @api public
|
15
|
-
module Right
|
15
|
+
module Right # rubocop:disable Metrics/ModuleLength
|
16
16
|
# @private
|
17
17
|
def self.included(m)
|
18
18
|
super
|
@@ -90,6 +90,16 @@ module Dry
|
|
90
90
|
self
|
91
91
|
end
|
92
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
|
+
|
93
103
|
# A lifted version of `#or`. For {RightBiased::Right} acts in the same way as `#or`,
|
94
104
|
# that is returns itselt.
|
95
105
|
#
|
@@ -118,18 +128,18 @@ module Dry
|
|
118
128
|
# create_user.apply(name) # => Failure(:name_missing)
|
119
129
|
#
|
120
130
|
# @return [RightBiased::Left,RightBiased::Right]
|
121
|
-
def apply(val = Undefined)
|
131
|
+
def apply(val = Undefined, &block)
|
122
132
|
unless @value.respond_to?(:call)
|
123
133
|
raise TypeError, "Cannot apply #{val.inspect} to #{@value.inspect}"
|
124
134
|
end
|
125
135
|
|
126
|
-
Undefined.default(val)
|
136
|
+
Undefined.default(val, &block).fmap { |unwrapped| curry.(unwrapped) }
|
127
137
|
end
|
128
138
|
|
129
139
|
# @param other [Object]
|
130
140
|
# @return [Boolean]
|
131
141
|
def ===(other)
|
132
|
-
self.class
|
142
|
+
other.instance_of?(self.class) && value! === other.value!
|
133
143
|
end
|
134
144
|
|
135
145
|
# Maps the value to Dry::Monads::Unit, useful when you don't care
|
@@ -197,11 +207,11 @@ module Dry
|
|
197
207
|
# @api private
|
198
208
|
def deconstruct
|
199
209
|
if Unit.equal?(@value)
|
200
|
-
|
201
|
-
elsif
|
202
|
-
@value
|
203
|
-
else
|
210
|
+
EMPTY_ARRAY
|
211
|
+
elsif !@value.is_a?(::Array)
|
204
212
|
[@value]
|
213
|
+
else
|
214
|
+
@value
|
205
215
|
end
|
206
216
|
end
|
207
217
|
|
@@ -226,7 +236,7 @@ module Dry
|
|
226
236
|
|
227
237
|
private
|
228
238
|
|
229
|
-
if RUBY_VERSION >=
|
239
|
+
if RUBY_VERSION >= "2.7"
|
230
240
|
# @api private
|
231
241
|
def destructure(value)
|
232
242
|
if value.is_a?(::Hash)
|
@@ -255,7 +265,7 @@ module Dry
|
|
255
265
|
# @private
|
256
266
|
# @return [String] Caller location
|
257
267
|
def self.trace_caller
|
258
|
-
caller_locations(2,
|
268
|
+
caller_locations(2, 1)[0].to_s
|
259
269
|
end
|
260
270
|
|
261
271
|
# Raises an error on accessing internal value
|
@@ -299,11 +309,20 @@ module Dry
|
|
299
309
|
raise NotImplementedError
|
300
310
|
end
|
301
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
|
+
|
302
321
|
# A lifted version of `#or`. This is basically `#or` + `#fmap`.
|
303
322
|
#
|
304
323
|
# @example
|
305
|
-
# Dry::Monads.None.
|
306
|
-
# 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)
|
307
326
|
#
|
308
327
|
# @return [RightBiased::Left, RightBiased::Right]
|
309
328
|
def or_fmap(*)
|
data/lib/dry/monads/task.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "concurrent/promise"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
5
|
+
require "dry/monads/unit"
|
6
|
+
require "dry/monads/curry"
|
7
|
+
require "dry/monads/conversion_stubs"
|
8
8
|
|
9
9
|
module Dry
|
10
10
|
module Monads
|
@@ -48,9 +48,11 @@ module Dry
|
|
48
48
|
# @example using a predefined executor
|
49
49
|
# Task[:fast] { do_quick_task }
|
50
50
|
#
|
51
|
-
# @param executor [Concurrent::AbstractExecutorService,Symbol]
|
52
|
-
#
|
53
|
-
#
|
51
|
+
# @param executor [Concurrent::AbstractExecutorService,Symbol]
|
52
|
+
# Either an executor instance
|
53
|
+
# or a name of predefined global
|
54
|
+
# from concurrent-ruby
|
55
|
+
#
|
54
56
|
# @return [Task]
|
55
57
|
def [](executor, &block)
|
56
58
|
new(Promise.execute(executor: executor, &block))
|
@@ -131,14 +133,14 @@ module Dry
|
|
131
133
|
state = case promise.state
|
132
134
|
when :fulfilled
|
133
135
|
if Unit.equal?(value!)
|
134
|
-
|
136
|
+
"value=()"
|
135
137
|
else
|
136
138
|
"value=#{value!.inspect}"
|
137
139
|
end
|
138
140
|
when :rejected
|
139
141
|
"error=#{promise.reason.inspect}"
|
140
142
|
else
|
141
|
-
|
143
|
+
"?"
|
142
144
|
end
|
143
145
|
|
144
146
|
"Task(#{state})"
|
@@ -164,14 +166,12 @@ module Dry
|
|
164
166
|
)
|
165
167
|
|
166
168
|
promise.on_error do |v|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
child.on_reject(e)
|
174
|
-
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)
|
175
175
|
end
|
176
176
|
promise.on_success { |v| child.on_fulfill(v) }
|
177
177
|
|
@@ -236,8 +236,8 @@ module Dry
|
|
236
236
|
#
|
237
237
|
# @param val [Task]
|
238
238
|
# @return [Task]
|
239
|
-
def apply(val = Undefined)
|
240
|
-
arg = Undefined.default(val)
|
239
|
+
def apply(val = Undefined, &block)
|
240
|
+
arg = Undefined.default(val, &block)
|
241
241
|
bind { |f| arg.fmap { |v| curry(f).(v) } }
|
242
242
|
end
|
243
243
|
|
@@ -314,7 +314,7 @@ module Dry
|
|
314
314
|
end
|
315
315
|
end
|
316
316
|
|
317
|
-
require
|
317
|
+
require "dry/monads/registry"
|
318
318
|
register_mixin(:task, Task::Mixin)
|
319
319
|
end
|
320
320
|
end
|
@@ -27,7 +27,8 @@ module Dry
|
|
27
27
|
# Lifts a block/proc over the 3-level nested structure.
|
28
28
|
#
|
29
29
|
# @example
|
30
|
-
# List[Right(Some(1)), Left(Some(1))].fmap3 { |x| x + 1 }
|
30
|
+
# List[Right(Some(1)), Left(Some(1))].fmap3 { |x| x + 1 }
|
31
|
+
# # => List[Right(Some(2)), Left(Some(1))]
|
31
32
|
# Right(None).fmap3 { |x| x + 1 } # => Right(None)
|
32
33
|
#
|
33
34
|
# @param args [Array<Object>] arguments will be passed to the block or the proc
|
data/lib/dry/monads/traverse.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# rubocop:disable Naming/ConstantName
|
4
|
+
# rubocop:disable Style/MutableConstant
|
5
|
+
|
6
|
+
require "dry/monads/validated"
|
4
7
|
|
5
8
|
module Dry
|
6
9
|
module Monads
|
@@ -20,3 +23,6 @@ module Dry
|
|
20
23
|
Traverse.freeze
|
21
24
|
end
|
22
25
|
end
|
26
|
+
|
27
|
+
# rubocop:enable Style/MutableConstant
|
28
|
+
# rubocop:enable Naming/ConstantName
|
data/lib/dry/monads/try.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/core/deprecations"
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require "dry/monads/right_biased"
|
7
|
+
require "dry/monads/conversion_stubs"
|
8
8
|
|
9
9
|
module Dry
|
10
10
|
module Monads
|
@@ -22,7 +22,7 @@ module Dry
|
|
22
22
|
attr_reader :exception
|
23
23
|
|
24
24
|
class << self
|
25
|
-
extend Core::Deprecations[:
|
25
|
+
extend Core::Deprecations[:"dry-monads"]
|
26
26
|
|
27
27
|
# Invokes a callable and if successful stores the result in the
|
28
28
|
# {Try::Value} type, but if one of the specified exceptions was raised it stores
|
@@ -72,7 +72,7 @@ module Dry
|
|
72
72
|
# @param exceptions [Array<Exception>]
|
73
73
|
# @return [Try::Value,Try::Error]
|
74
74
|
def [](*exceptions, &block)
|
75
|
-
raise ArgumentError,
|
75
|
+
raise ArgumentError, "At least one exception type required" if exceptions.empty?
|
76
76
|
|
77
77
|
run(exceptions, block)
|
78
78
|
end
|
@@ -92,7 +92,7 @@ module Dry
|
|
92
92
|
|
93
93
|
# Returns self.
|
94
94
|
#
|
95
|
-
# @return [
|
95
|
+
# @return [Try::Value, Try::Error]
|
96
96
|
def to_monad
|
97
97
|
self
|
98
98
|
end
|
@@ -110,6 +110,8 @@ module Dry
|
|
110
110
|
# @param exceptions [Array<Exception>] list of exceptions to be rescued
|
111
111
|
# @param value [Object] the value to be stored in the monad
|
112
112
|
def initialize(exceptions, value)
|
113
|
+
super()
|
114
|
+
|
113
115
|
@catchable = exceptions
|
114
116
|
@value = value
|
115
117
|
end
|
@@ -160,12 +162,21 @@ module Dry
|
|
160
162
|
# @return [String]
|
161
163
|
def to_s
|
162
164
|
if Unit.equal?(@value)
|
163
|
-
|
165
|
+
"Try::Value()"
|
164
166
|
else
|
165
167
|
"Try::Value(#{@value.inspect})"
|
166
168
|
end
|
167
169
|
end
|
168
170
|
alias_method :inspect, :to_s
|
171
|
+
|
172
|
+
# Ignores values and returns self, see {Try::Error#recover}
|
173
|
+
#
|
174
|
+
# @param errors [Class] List of Exception subclasses
|
175
|
+
#
|
176
|
+
# @return [Try::Value]
|
177
|
+
def recover(*_errors)
|
178
|
+
self
|
179
|
+
end
|
169
180
|
end
|
170
181
|
|
171
182
|
# Represents a result of a failed execution.
|
@@ -179,6 +190,8 @@ module Dry
|
|
179
190
|
|
180
191
|
# @param exception [Exception]
|
181
192
|
def initialize(exception)
|
193
|
+
super()
|
194
|
+
|
182
195
|
@exception = exception
|
183
196
|
end
|
184
197
|
|
@@ -211,6 +224,26 @@ module Dry
|
|
211
224
|
def ===(other)
|
212
225
|
Error === other && exception === other.exception
|
213
226
|
end
|
227
|
+
|
228
|
+
# Acts in a similar way to `rescue`. It checks if
|
229
|
+
# {exception} is one of {errors} and yields the block if so.
|
230
|
+
#
|
231
|
+
# @param errors [Class] List of Exception subclasses
|
232
|
+
#
|
233
|
+
# @return [Try::Value]
|
234
|
+
def recover(*errors)
|
235
|
+
if errors.empty?
|
236
|
+
classes = DEFAULT_EXCEPTIONS
|
237
|
+
else
|
238
|
+
classes = errors
|
239
|
+
end
|
240
|
+
|
241
|
+
if classes.any? { |c| c === exception }
|
242
|
+
Value.new([exception.class], yield(exception))
|
243
|
+
else
|
244
|
+
self
|
245
|
+
end
|
246
|
+
end
|
214
247
|
end
|
215
248
|
|
216
249
|
# A module that can be included for easier access to Try monads.
|
@@ -261,9 +294,9 @@ module Dry
|
|
261
294
|
#
|
262
295
|
def Value(value = Undefined, exceptions = DEFAULT_EXCEPTIONS, &block)
|
263
296
|
v = Undefined.default(value, block)
|
264
|
-
raise ArgumentError,
|
297
|
+
raise ArgumentError, "No value given" if !value.nil? && v.nil?
|
265
298
|
|
266
|
-
Value.new(exceptions, v)
|
299
|
+
Try::Value.new(exceptions, v)
|
267
300
|
end
|
268
301
|
|
269
302
|
# Error constructor
|
@@ -278,14 +311,14 @@ module Dry
|
|
278
311
|
#
|
279
312
|
def Error(error = Undefined, &block)
|
280
313
|
v = Undefined.default(error, block)
|
281
|
-
raise ArgumentError,
|
314
|
+
raise ArgumentError, "No value given" if v.nil?
|
282
315
|
|
283
316
|
Try::Error.new(v)
|
284
317
|
end
|
285
318
|
end
|
286
319
|
end
|
287
320
|
|
288
|
-
require
|
321
|
+
require "dry/monads/registry"
|
289
322
|
register_mixin(:try, Try::Mixin)
|
290
323
|
end
|
291
324
|
end
|
data/lib/dry/monads/unit.rb
CHANGED