Class: MonadOxide::Err

Inherits:
Result
  • Object
show all
Defined in:
lib/err.rb

Overview

Err is the error case for Result.

Any methods in Result that would process a successful Result (Ok) will fall through with Err instances.

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Result

#flatten, #match

Constructor Details

#initialize(data) ⇒ Err

Create an Err.

If the Exception provided was not thrown (ie. created with Exception.new), it will not have a backtrace. The Err constructor takes care of this by raising the Exception and immediately capturing the Exception - this causes the backtrace to be populated and will be availabl to any cosumers automatically.

Parameters:

  • data (T|Array<T>)

    The data for this Err. If it is an Exception or an Array of Exceptions, this will add a backtrace if it the Exceptions do not have backtraces already. Exception it will result in a TypeError.



50
51
52
53
54
55
56
# File 'lib/err.rb', line 50

def initialize(data)
  if data.kind_of?(Array)
    @data = data.map(&self.class.method(:backtrace_fix))
  else
    @data = self.class.backtrace_fix(data)
  end
end

Class Method Details

.backtrace_fix(x) ⇒ Object

Add a backtrace to an Exception, or just pass long whatever was given. The Exception is mutated in the process.

Ruby Exceptions do not come with a backtrace. During the act of raising an Exception, that Exception is granted a backtrace. So any kind of ‘Exception.new()’ invocations will have a ‘nil’ value for ‘backtrace()’. To get around this, we can simply raise the Exception here, and then we get the backtrace we want.

On a cursory search, this is not documented behavior.

Parameters:

  • x (Exception|T)

    The potential Exception to add a backtrace to, if it is missing a backtrace.



26
27
28
29
30
31
32
33
34
# File 'lib/err.rb', line 26

def self.backtrace_fix(x)
  if x.kind_of?(Exception) && x.backtrace.nil?
    raise x
  else
    x
  end
rescue => e
  e
end

Instance Method Details

#and_then(f = nil) { ... } ⇒ Err

Falls through. @see Result#and_then for how this is handled in either Result case, and @see Ok.and_then for how this is handled in the Ok case.

Parameters:

  • f (Proc) (defaults to: nil)

    Optional Proc - ignored.

Yields:

  • An ignored block.

Returns:

  • (Err)

    This Err.



64
65
66
# File 'lib/err.rb', line 64

def and_then(f=nil, &block)
  self
end

#err?Boolean

Identifies that this is an ‘Err`. For counterparts:

Returns:

  • (Boolean)

    ‘true` because this is an `Err`.

See Also:



75
76
77
# File 'lib/err.rb', line 75

def err?()
  true
end

#inspect_err(f = nil) { ... } ⇒ Result<A, E>

Applies ‘f’ or the block over the ‘Exception’ and returns the same ‘Err’. No changes are applied. This is ideal for logging. Exceptions raised during these transformations will return an ‘Err’ with the Exception.

Parameters:

  • f (Proc<A=Exception>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A] the return is ignored.

Yields:

  • Will yield a block that takes an A the return is ignored. Same as ‘f’ parameter.

Returns:

  • (Result<A, E>)

    returns self.



88
89
90
91
92
93
94
95
# File 'lib/err.rb', line 88

def inspect_err(f=nil, &block)
  begin
    (f || block).call(@data)
    self
  rescue => e
    self.class.new(e)
  end
end

#inspect_ok(f = nil) { ... } ⇒ Err

Falls through. @see Result#inspect_ok for how this is handled in either Result case, and @see Ok.inspect_ok for how this is handled in the Ok case.

Parameters:

  • f (Proc) (defaults to: nil)

    Optional Proc - ignored.

Yields:

  • An ignored block.

Returns:

  • (Err)

    This Err.



104
105
106
# File 'lib/err.rb', line 104

def inspect_ok(f=nil, &block)
  self
end

#map(f = nil) { ... } ⇒ Err

Falls through. @see Result#map for how this is handled in either Result case, and @see Ok.map for how this is handled in the Ok case.

Parameters:

  • f (Proc) (defaults to: nil)

    Optional Proc - ignored.

Yields:

  • An ignored block.

Returns:

  • (Err)

    This Err.



114
115
116
# File 'lib/err.rb', line 114

def map(f=nil, &block)
  self
end

#map_err(f = nil) { ... } ⇒ Result<B>

Applies ‘f’ or the block over the data and returns a new new ‘Err’ with the returned value.

Parameters:

  • f (Proc<A, B>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Object] and returns a B.

Yields:

  • Will yield a block that takes an A and returns an Err<B>. Same as ‘f’ parameter.

Returns:

  • (Result<B>)

    A new ‘Err<B>’ whose ‘B’ is the return of ‘f’ or the block. Errors raised when applying ‘f’ or the block will result in a returned ‘Err<Exception>’.



128
129
130
131
132
133
134
# File 'lib/err.rb', line 128

def map_err(f=nil, &block)
  begin
    self.class.new((f || block).call(@data))
  rescue => e
    self.class.new(e)
  end
end

#ok?Boolean

Identifies that this is not an ‘Ok`. For counterparts:

Returns:

  • (Boolean)

    ‘false` because this is not an `Ok`.

See Also:



143
144
145
# File 'lib/err.rb', line 143

def ok?()
  false
end

#or_else(f = nil) { ... } ⇒ Err<C> | Err<Exception>

Invokes ‘f’ or the block with the data and returns the Result returned from that. Exceptions raised during ‘f’ or the block will return an ‘Err<Exception>’. The return type is enforced.

Parameters:

  • f (Proc<A, Result<B>>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Object] and must return a [Result<B>].

Yields:

  • Will yield a block that takes an A and returns a Result<B>. Same as ‘f’ parameter.

Returns:

  • (Err<C> | Err<Exception>)

    A new Result from ‘f’ or the block. Exceptions raised will result in ‘Err<Exception>’. If ‘f’ returns a non-Result, this will return ‘Err<ResultReturnExpectedError>’.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/err.rb', line 158

def or_else(f=nil, &block)
  begin
    r = (f || block).call(@data)
    # Enforce that we always get a Result. Without a Result, coerce to an
    # Err.
    if !r.kind_of?(Result)
      raise ResultReturnExpectedError.new(r)
    else
      r
    end
  rescue => e
    Err.new(e)
  end
end

#unwrapA

Dangerously try to access the ‘Result’ data. If this is an ‘Err’, an exception will be raised. It is recommended to use this for tests only.

Returns:

  • (A)

    The inner data of this ‘Result’.

Raises:



177
178
179
180
181
# File 'lib/err.rb', line 177

def unwrap()
  raise UnwrapError.new(
    "#{self.class} with #{@data.inspect} could not be unwrapped as an Ok.",
  )
end

#unwrap_errE

Dangerously access the ‘Err’ data. If this is an ‘Ok’, an exception will be raised. It is recommended to use this for tests only.

Returns:

  • (E)

    The ‘Exception’ of this ‘Err’.



187
188
189
# File 'lib/err.rb', line 187

def unwrap_err()
  @data
end

#unwrap_err_or_else(f = nil, &_block) ⇒ T

Safely unwrap the ‘Result`. In the case of `Err`, this returns the wrapped value.

Parameters:

  • _f

    A dummy function. Not used.

Returns:

  • (T)

    The wrapped value.



197
198
199
# File 'lib/err.rb', line 197

def unwrap_err_or_else(f=nil, &_block)
  @data
end

#unwrap_or(x) ⇒ T

Safely unwrap the ‘Result`. In the case of `Err`, this returns the provided default value.

Parameters:

  • x (T)

    The value that will be returned.

Returns:

  • (T)

    The ‘x` value.



207
208
209
# File 'lib/err.rb', line 207

def unwrap_or(x)
  x
end

#unwrap_or_else(f = nil) { ... } ⇒ B

Safely unwrap the ‘Result`. In the case of `Err`, this uses the provided function to produce a value.

Parameters:

  • f (Proc<B>) (defaults to: nil)

    The function to call. Could be a block instead. Takes nothing and returns a [B=Object].

Yields:

  • Will yield a block that takes nothing and returns a [B=Object]. Same as ‘f’ parameter.

Returns:

  • (B)

    The value returned from ‘f`.



220
221
222
# File 'lib/err.rb', line 220

def unwrap_or_else(f=nil, &block)
  (f || block).call()
end