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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +140 -80
  3. data/LICENSE +1 -1
  4. data/README.md +5 -4
  5. data/dry-monads.gemspec +30 -30
  6. data/lib/dry-monads.rb +1 -1
  7. data/lib/dry/monads.rb +2 -2
  8. data/lib/dry/monads/all.rb +2 -2
  9. data/lib/dry/monads/constants.rb +1 -1
  10. data/lib/dry/monads/do.rb +52 -18
  11. data/lib/dry/monads/do/all.rb +36 -17
  12. data/lib/dry/monads/either.rb +7 -7
  13. data/lib/dry/monads/errors.rb +5 -2
  14. data/lib/dry/monads/lazy.rb +15 -4
  15. data/lib/dry/monads/list.rb +28 -28
  16. data/lib/dry/monads/maybe.rb +87 -19
  17. data/lib/dry/monads/registry.rb +10 -10
  18. data/lib/dry/monads/result.rb +38 -12
  19. data/lib/dry/monads/result/fixed.rb +33 -24
  20. data/lib/dry/monads/right_biased.rb +35 -16
  21. data/lib/dry/monads/task.rb +20 -20
  22. data/lib/dry/monads/transformer.rb +2 -1
  23. data/lib/dry/monads/traverse.rb +7 -1
  24. data/lib/dry/monads/try.rb +45 -12
  25. data/lib/dry/monads/unit.rb +6 -2
  26. data/lib/dry/monads/validated.rb +20 -16
  27. data/lib/dry/monads/version.rb +1 -1
  28. data/lib/json/add/dry/monads/maybe.rb +3 -3
  29. metadata +18 -69
  30. data/.codeclimate.yml +0 -12
  31. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  32. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -30
  33. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  34. data/.github/workflows/ci.yml +0 -52
  35. data/.github/workflows/docsite.yml +0 -34
  36. data/.github/workflows/sync_configs.yml +0 -56
  37. data/.gitignore +0 -10
  38. data/.rspec +0 -4
  39. data/.rubocop.yml +0 -101
  40. data/.yardopts +0 -4
  41. data/CODE_OF_CONDUCT.md +0 -13
  42. data/CONTRIBUTING.md +0 -29
  43. data/Gemfile +0 -19
  44. data/Gemfile.devtools +0 -14
  45. data/Rakefile +0 -8
  46. data/bin/.gitkeep +0 -0
  47. data/bin/console +0 -17
  48. data/bin/setup +0 -7
  49. data/docsite/source/case-equality.html.md +0 -42
  50. data/docsite/source/do-notation.html.md +0 -207
  51. data/docsite/source/getting-started.html.md +0 -142
  52. data/docsite/source/index.html.md +0 -179
  53. data/docsite/source/list.html.md +0 -87
  54. data/docsite/source/maybe.html.md +0 -146
  55. data/docsite/source/pattern-matching.html.md +0 -68
  56. data/docsite/source/result.html.md +0 -190
  57. data/docsite/source/task.html.md +0 -126
  58. data/docsite/source/tracing-failures.html.md +0 -32
  59. data/docsite/source/try.html.md +0 -76
  60. data/docsite/source/unit.html.md +0 -36
  61. data/docsite/source/validated.html.md +0 -88
  62. data/log/.gitkeep +0 -0
  63. data/project.yml +0 -2
@@ -1,37 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dry::Monads
4
- class Result
5
- # @see Monads#Result
6
- # @private
7
- class Fixed < Module
8
- def self.[](error, **options)
9
- new(error, **options)
10
- end
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
- def initialize(error, **_options)
13
- @mod = Module.new do
14
- define_method(:Failure) do |value|
15
- if error === value
16
- Failure.new(value, RightBiased::Left.trace_caller)
17
- else
18
- raise InvalidFailureTypeError, value
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
- def Success(value = Undefined, &block)
23
- v = Undefined.default(value, block || Unit)
24
- Success.new(v)
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
- private
37
+ private
30
38
 
31
- def included(base)
32
- super
39
+ def included(base)
40
+ super
33
41
 
34
- base.include(@mod)
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 'dry/monads/constants'
4
- require 'dry/monads/unit'
5
- require 'dry/monads/curry'
6
- require 'dry/monads/errors'
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) { yield }.fmap { |unwrapped| curry.(unwrapped) }
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 == other.class && value! === other.value!
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 @value.is_a?(::Array)
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 >= '2.7'
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, 2)[0].to_s
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.or('no value') # => Some("no value")
306
- # Dry::Monads.None.or { Time.now } # => Some(current time)
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(*)
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/promise'
3
+ require "concurrent/promise"
4
4
 
5
- require 'dry/monads/unit'
6
- require 'dry/monads/curry'
7
- require 'dry/monads/conversion_stubs'
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] Either an executor instance
52
- # or a name of predefined global
53
- # from concurrent-ruby
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
- 'value=()'
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
- begin
168
- inner = block.(v).promise
169
- inner.execute
170
- inner.on_success { |r| child.on_fulfill(r) }
171
- inner.on_error { |e| child.on_reject(e) }
172
- rescue StandardError => e
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) { yield }
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 'dry/monads/registry'
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 } # => List[Right(Some(2)), Left(Some(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
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/validated'
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
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/equalizer'
4
- require 'dry/core/deprecations'
3
+ require "dry/core/equalizer"
4
+ require "dry/core/deprecations"
5
5
 
6
- require 'dry/monads/right_biased'
7
- require 'dry/monads/conversion_stubs'
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[:'dry-monads']
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, 'At least one exception type required' if exceptions.empty?
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 [Maybe::Some, Maybe::None]
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
- 'Try::Value()'
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, 'No value given' if !value.nil? && v.nil?
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, 'No value given' if v.nil?
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 'dry/monads/registry'
321
+ require "dry/monads/registry"
289
322
  register_mixin(:try, Try::Mixin)
290
323
  end
291
324
  end
@@ -20,11 +20,15 @@ module Dry
20
20
  #
21
21
  Unit = Object.new.tap do |unit|
22
22
  def unit.to_s
23
- 'Unit'
23
+ "Unit"
24
24
  end
25
25
 
26
26
  def unit.inspect
27
- 'Unit'
27
+ "Unit"
28
+ end
29
+
30
+ def unit.deconstruct
31
+ EMPTY_ARRAY
28
32
  end
29
33
  end
30
34
  end