Class: Result

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

Overview

NOTE: This class is intentionally not namespaced to allow for more concise, readable, and explicit usage.

It it a generic reusable implementation of the Result type, and is not specific to any domain

rubocop:disable Gitlab/NamespacedClass

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.err(err_value) ⇒ Result

“self.err” corresponds to Err(E) in Rust: doc.rust-lang.org/std/result/enum.Result.html#variant.Err

Parameters:

  • ok_value (Object, #new)

Returns:



34
35
36
# File 'lib/result.rb', line 34

def self.err(err_value)
  new(err_value: err_value)
end

.ok(ok_value) ⇒ Result

The .ok and .err factory class methods are the only way to create a Result

“self.ok” corresponds to Ok(T) in Rust: doc.rust-lang.org/std/result/enum.Result.html#variant.Ok

Parameters:

  • ok_value (Object, #new)

Returns:



26
27
28
# File 'lib/result.rb', line 26

def self.ok(ok_value)
  new(ok_value: ok_value)
end

Instance Method Details

#==(other) ⇒ Boolean

Returns:

  • (Boolean)


168
169
170
171
# File 'lib/result.rb', line 168

def ==(other)
  # NOTE: The underlying `@ok` instance variable is a boolean, so we only need to check `ok?`, not `err?` too
  self.class == other.class && other.ok? == ok? && other.instance_variable_get(:@value) == value
end

#and_then(lambda_or_singleton_method) ⇒ Result

‘and_then` is a functional way to chain together operations which may succeed or have errors. It is passed a lambda or class (singleton) method object, and must return a Result object representing “ok” or “err”.

If the Result object it is called on is “ok”, then the passed lambda or singleton method is called with the value contained in the Result.

If the Result object it is called on is “err”, then it is returned without calling the passed lambda or method.

It only supports being passed a lambda, or a class (singleton) method object which responds to ‘call` with a single argument (arity of 1). If multiple values are needed, pass a hash or array. Note that passing `Proc` objects is NOT supported, even though the YARD annotation contains `Proc` (because the type of a lambda is also `Proc`).

Passing instance methods to ‘and_then` is not supported, because the methods in the chain should be stateless “pure functions”, and should not be persisting or referencing any instance state anyway.

“#and_then” corresponds to “and_then” in Rust: doc.rust-lang.org/std/result/enum.Result.html#method.and_then

Parameters:

  • lambda_or_singleton_method (Proc, Method)

Returns:

Raises:

  • (TypeError)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/result.rb', line 94

def and_then(lambda_or_singleton_method)
  validate_lambda_or_singleton_method(lambda_or_singleton_method)

  # Return/passthough the Result itself if it is an err
  return self if err?

  # If the Result is ok, call the lambda or singleton method with the contained value
  result = lambda_or_singleton_method.call(value)

  unless result.is_a?(Result)
    err_msg = "'Result##{__method__}' expects a lambda or singleton method object which returns a 'Result' type " \
              ", but instead received '#{lambda_or_singleton_method.inspect}' which returned '#{result.class}'. " \
              "Check that the previous method calls in the '#and_then' chain are correct."
    raise(TypeError, err_msg)
  end

  result
end

#deconstruct_keys(keys) ⇒ Hash

‘deconstruct_keys` supports pattern matching on a Result object with a `case` statement. See specs for examples.

Parameters:

  • keys (Array)

Returns:

  • (Hash)

Raises:

  • (ArgumentError)


161
162
163
164
165
# File 'lib/result.rb', line 161

def deconstruct_keys(keys)
  raise(ArgumentError, 'Use either :ok or :err for pattern matching') unless [[:ok], [:err]].include?(keys)

  to_h
end

#err?Boolean

The ‘err?` attribute will be false if the Result was constructed with .ok, and true if it was constructed with .err “#err?” corresponds to “is_err” in Rust.

Returns:

  • (Boolean)


67
68
69
# File 'lib/result.rb', line 67

def err?
  !ok?
end

#map(lambda_or_singleton_method) ⇒ Result

‘map` is similar to `and_then`, but it is used for “single track” methods which always succeed, and have no possibility of returning an error (but they may still raise exceptions, which is unrelated to the Result handling). The passed lambda or singleton method must return a value, not a Result.

If the Result object it is called on is “ok”, then the passed lambda or singleton method is called with the value contained in the Result.

If the Result object it is called on is “err”, then it is returned without calling the passed lambda or method.

“#map” corresponds to “map” in Rust: doc.rust-lang.org/std/result/enum.Result.html#method.map

Parameters:

  • lambda_or_singleton_method (Proc, Method)

Returns:

Raises:

  • (TypeError)


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/result.rb', line 129

def map(lambda_or_singleton_method)
  validate_lambda_or_singleton_method(lambda_or_singleton_method)

  # Return/passthrough the Result itself if it is an err
  return self if err?

  # If the Result is ok, call the lambda or singleton method with the contained value
  mapped_value = lambda_or_singleton_method.call(value)

  if mapped_value.is_a?(Result)
    err_msg = "'Result##{__method__}' expects a lambda or singleton method object which returns an unwrapped " \
              "value, not a 'Result', but instead received '#{lambda_or_singleton_method.inspect}' which returned " \
              "a 'Result'."
    raise(TypeError, err_msg)
  end

  # wrap the returned mapped_value in an "ok" Result.
  Result.ok(mapped_value)
end

#ok?Boolean

The ‘ok?` attribute will be true if the Result was constructed with .ok, and false if it was constructed with .err

“#ok?” corresponds to “is_ok” in Rust.

Returns:

  • (Boolean)


58
59
60
61
# File 'lib/result.rb', line 58

def ok?
  # We don't make `@ok` an attr_reader, because we don't want to confusingly shadow the class method `.ok`
  @ok
end

#to_hHash

‘to_h` supports destructuring of a result object, for example: `result => { ok: }; puts ok`

Returns:

  • (Hash)


152
153
154
# File 'lib/result.rb', line 152

def to_h
  ok? ? { ok: value } : { err: value }
end

#unwrapObject

“#unwrap” corresponds to “unwrap” in Rust.

Returns:

  • (Object)

Raises:

  • (RuntimeError)

    if called on an “err” Result



42
43
44
# File 'lib/result.rb', line 42

def unwrap
  ok? ? value : raise("Called Result#unwrap on an 'err' Result")
end

#unwrap_errObject

“#unwrap” corresponds to “unwrap” in Rust.

Returns:

  • (Object)

Raises:

  • (RuntimeError)

    if called on an “ok” Result



50
51
52
# File 'lib/result.rb', line 50

def unwrap_err
  err? ? value : raise("Called Result#unwrap_err on an 'ok' Result")
end