Class: Errgonomic::Result::Any

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

Overview

The base class for Result’s Ok and Err class variants. We implement as much logic as possible here, and let Ok and Err handle their initialization and self identification.

Direct Known Subclasses

Err, Ok

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Any

Returns a new instance of Any.



11
12
13
# File 'lib/errgonomic/result.rb', line 11

def initialize(value)
  @value = value
end

Instance Attribute Details

#valueObject (readonly)

Returns the value of attribute value.



9
10
11
# File 'lib/errgonomic/result.rb', line 9

def value
  @value
end

Instance Method Details

#==(other) ⇒ Object

Equality comparison for Result objects is based on value not reference.

Examples:

Ok(1) == Ok(1) # => true
Ok(1) == Err(1) # => false
Ok(1).object_id == Ok(1).object_id # => false
Ok(1) == 1 # => false
Err() == nil # => false

Parameters:



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

def ==(other)
  return false if self.class != other.class

  value == other.value
end

#and(other) ⇒ Object

Given another result, return it if the inner result is Ok, else return the inner Err. Raise an exception if the other value is not a Result.

Examples:

Ok(1).and(Ok(2)) # => Ok(2)
Ok(1).and(Err(:f)) # => Err(:f)
Err(:g).and(Ok(1)) # => Err(:g)
Err(:h).and(Err(:i)) # => Err(:h)
Ok(1).and(2) # => raise Errgonomic::ArgumentError, "other must be a Result"

Parameters:

Raises:



121
122
123
124
125
126
# File 'lib/errgonomic/result.rb', line 121

def and(other)
  raise Errgonomic::ArgumentError, 'other must be a Result' unless other.is_a?(Errgonomic::Result::Any)
  return self if err?

  other
end

#and_then(&block) ⇒ Object

Given a block, evaluate it and return its result if the inner result is Ok, else return the inner Err. This is lazy evaluated, and we pedantically check the type of the block’s return value at runtime. This is annoying, sorry, but better than an “undefined method” error. Hopefully it gives your test suite a chance to detect incorrect usage.

Examples:

Ok(1).and_then { |x| Ok(x + 1) } # => Ok(2)
Ok(1).and_then { |_| Err(:error) } # => Err(:error)
Err(:error).and_then { |x| Ok(x + 1) } # => Err(:error)
Err(:error).and_then { |x| Err(:error2) } # => Err(:error)

Parameters:

  • block (Proc)


141
142
143
144
145
146
147
148
149
150
# File 'lib/errgonomic/result.rb', line 141

def and_then(&block)
  return self if err?

  res = block.call(value)
  if !res.is_a?(Errgonomic::Result::Any) && !Errgonomic.give_me_ambiguous_downstream_errors?
    raise Errgonomic::ArgumentError, 'and_then block must return a Result'
  end

  res
end

#deconstructObject

Examples:

simple pattern match with variable capture of the value

result = Errgonomic::Result::Ok.new(1)
case result
in Errgonomic::Result::Ok, value
  "Measurement is #{value}"
in Errgonomic::Result::Err, err
  "Measurement is not available"
end # => "Measurement is 1"

more advanced pattern match against the kind of value

result = Errgonomic::Result::Err.new(StandardError.new("nope"))
case result
in Errgonomic::Result::Ok, value
  "Measurement is #{value}"
in Errgonomic::Result::Err, String => msg
  "Measurement failed with a message: #{msg}"
in Errgonomic::Result::Err, Exception => e
  "Measurement produced an exception -- #{e.class}: #{e}"
end # => "Measurement produced an exception -- StandardError: nope"


305
306
307
# File 'lib/errgonomic/result.rb', line 305

def deconstruct
  [self, value]
end

#err_and?(&block) ⇒ Boolean

Return true if the inner value is an Err and the result of the block is truthy.

Examples:

Ok(1).err_and?(&:odd?) # => false
Ok(1).err_and?(&:even?) # => false
Err(:a).err_and? { |_| true } # => true
Err(:b).err_and? { |_| false } # => false

Returns:

  • (Boolean)


66
67
68
69
70
71
72
# File 'lib/errgonomic/result.rb', line 66

def err_and?(&block)
  if err?
    !!block.call(value)
  else
    false
  end
end

#expect!(msg) ⇒ Object

Return the inner value of an Ok, else raise an exception with the given message when Err.

Examples:

Ok(1).expect!("should have worked") # => 1
Err(:d).expect!("should have worked") # => raise Errgonomic::ExpectError, "should have worked"

Parameters:

Raises:



93
94
95
96
97
# File 'lib/errgonomic/result.rb', line 93

def expect!(msg)
  raise Errgonomic::ExpectError, msg unless ok?

  @value
end

#map(&block) ⇒ Object

Map the Ok(a) to an Ok(b), preserving the Err

Examples:

Err(:broken).map { |_val| :nominal } # => Err(:broken)
Ok(:plausible).map { |_val| :success } # => Ok(:success)


247
248
249
250
251
# File 'lib/errgonomic/result.rb', line 247

def map(&block)
  return self if err?

  Ok(block.call(value))
end

#map_err(&block) ⇒ Object

Map the Err(e) to an Err(f), preserving the Ok

Examples:

Ok(:Alice).map_err { |_e| :Bob } # => Ok(:Alice)
Err(:bob).map_err { |e| e.capitalize } # => Err(:Bob)


258
259
260
261
262
# File 'lib/errgonomic/result.rb', line 258

def map_err(&block)
  return self if ok?

  Err(block.call(value))
end

#ok_and?(&block) ⇒ Boolean

Return true if the inner value is an Ok and the result of the block is truthy.

Examples:

Ok(1).ok_and?(&:odd?) # => true
Ok(1).ok_and?(&:even?) # => false
Err(:a).ok_and? { |_| true } # => false
Err(:b).ok_and? { |_| false } # => false

Parameters:

  • block (Proc)

    The block to evaluate if the inner value is an Ok.

Returns:

  • (Boolean)


52
53
54
55
56
# File 'lib/errgonomic/result.rb', line 52

def ok_and?(&block)
  return false if err?

  !!block.call(value)
end

#or(other) ⇒ Object

Return other if self is Err, else return the original Option. Raises a pedantic runtime exception if other is not a Result.

Examples:

Err(:j).or(Ok(1)) # => Ok(1)
Err(:k).or(Err(:l)) # => Err(:l)
Err(:m).or("oops") # => raise Errgonomic::ArgumentError, "other must be a Result; you might want unwrap_or"

Parameters:



161
162
163
164
165
166
167
168
169
# File 'lib/errgonomic/result.rb', line 161

def or(other)
  unless other.is_a?(Errgonomic::Result::Any)
    raise Errgonomic::ArgumentError,
          'other must be a Result; you might want unwrap_or'
  end
  return other if err?

  self
end

#or_else(&block) ⇒ Object

Return self if it is Ok, else lazy evaluate the block and return its result. Raises a pedantic runtime check that the block returns a Result. Sorry about that, hopefully it helps your tests. Better than ambiguous downstream “undefined method” errors, probably.

Examples:

Ok(1).or_else { |e| Ok(2) } # => Ok(1)
Err(:o).or_else { |e| Ok(1) } # => Ok(1)
Err(:q).or_else { |e| Err(:r) } # => Err(:r)
Err(:s).or_else { |e| "oops" } # => raise Errgonomic::ArgumentError, "or_else block must return a Result"

Parameters:

  • block (Proc)


183
184
185
186
187
188
189
190
191
192
# File 'lib/errgonomic/result.rb', line 183

def or_else(&block)
  return self if ok?

  res = block.call(value)
  if !res.is_a?(Errgonomic::Result::Any) && !Errgonomic.give_me_ambiguous_downstream_errors?
    raise Errgonomic::ArgumentError, 'or_else block must return a Result'
  end

  res
end

#result?Boolean

Indicate that this is some kind of result object. Contrast to Object#result? which is false for all other types.

Examples:

Ok("a").result? # => true
Err("a").result? # => true
"a".result? # => false

Returns:

  • (Boolean)


38
39
40
# File 'lib/errgonomic/result.rb', line 38

def result?
  true
end

#tap_err(&block) ⇒ Object

Calls the function with the inner error value, if Err, but returns the original Result.

Examples:

tapped = false
Ok(1).tap_err { |err| tapped = err } # => Ok(1)
tapped # => false
Err(:nope).tap_err { |err| tapped = err } # => Err(:nope)
tapped # => :nope


230
231
232
233
# File 'lib/errgonomic/result.rb', line 230

def tap_err(&block)
  block.call(value) if err?
  self
end

#tap_ok(&block) ⇒ Object

Calls the function with the inner ok value, if Ok, while returning the original Result.



237
238
239
240
# File 'lib/errgonomic/result.rb', line 237

def tap_ok(&block)
  block.call(value) if ok?
  self
end

#to_json(*_args) ⇒ Object

Refuse to serialize an unwrapped Result as JSON. Not only should we require that Results be correctly handled to access their inner value, but without this we will get undefined structures from default Object#to_json implementations.

Examples:

Ok("").to_json # => raise Errgonomic::SerializeError, "cannot serialize an unwrapped Result"
Err("").to_json # => raise Errgonomic::SerializeError, "cannot serialize an unwrapped Result"

Raises:



282
283
284
# File 'lib/errgonomic/result.rb', line 282

def to_json(*_args)
  raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Result'
end

#to_sObject

Refuse to serialize an unwrapped Result as a String. Results must be correctly handled to access their inner value.

Examples:

Ok("").to_s # => raise Errgonomic::SerializeError, "cannot serialize an unwrapped Result"
Err("").to_s # => raise Errgonomic::SerializeError, "cannot serialize an unwrapped Result"

Raises:



270
271
272
# File 'lib/errgonomic/result.rb', line 270

def to_s
  raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Result'
end

#unwrap!Object

Return the inner value of an Ok, else raise an exception when Err.

Examples:

Ok(1).unwrap! # => 1
Err(:c).unwrap! # => raise Errgonomic::UnwrapError.new("value is an Err", :c)

Raises:



79
80
81
82
83
# File 'lib/errgonomic/result.rb', line 79

def unwrap!
  raise Errgonomic::UnwrapError.new('value is an Err', @value) unless ok?

  @value
end

#unwrap_err!Object

Return the inner value of an Err, else raise an exception when Ok.

Examples:

Ok(1).unwrap_err! # => raise Errgonomic::UnwrapError, 1
Err(:e).unwrap_err! # => :e

Raises:



104
105
106
107
108
# File 'lib/errgonomic/result.rb', line 104

def unwrap_err!
  raise Errgonomic::UnwrapError, value unless err?

  @value
end

#unwrap_or(other) ⇒ Object

Return the inner value if self is Ok, else return the provided default.

Examples:

Ok(1).unwrap_or(2) # => 1
Err(:t).unwrap_or(:u) # => :u

Parameters:



201
202
203
204
205
# File 'lib/errgonomic/result.rb', line 201

def unwrap_or(other)
  return value if ok?

  other
end

#unwrap_or_else(&block) ⇒ Object

Return the inner value if self is Ok, else lazy evaluate the block and return its result.

Examples:

Ok(1).unwrap_or_else { 2 } # => 1
Err("foo").unwrap_or_else { |s| s.length } # => 3

Parameters:

  • block (Proc)


215
216
217
218
219
# File 'lib/errgonomic/result.rb', line 215

def unwrap_or_else(&block)
  return value if ok?

  block.call(value)
end