Class: Errgonomic::Option::Any

Inherits:
Object
  • Object
show all
Defined in:
lib/errgonomic/option.rb,
lib/errgonomic/rails/active_record_optional.rb

Overview

The base class for all options. Some and None are subclasses.

Direct Known Subclasses

None, Some

Instance Method Summary collapse

Instance Method Details

#==(other) ⇒ Object

An option of the same type with an equal inner value is equal.

Because we’re going to monkey patch this into other libraries Rails, we allow some “pass through” functionality into the inner value of a Some, such as comparability here.

TODO: does None == null?

strict:

Some(1) == 1 # => raise Errgonomic::NotComparableError, "Cannot compare Errgonomic::Option::Some with Integer"

Examples:

Some(1) == Some(1) # => true
Some(1) == Some(2) # => false
Some(1) == None() # => false
None() == None() # => true
Some(1) == 1 # => false
None() == nil # => false


30
31
32
33
34
35
# File 'lib/errgonomic/option.rb', line 30

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

  value == other.value
end

#and(other) ⇒ Object

If self is Some, return the provided other Option.

Examples:

None().and(Some(1)) # => None()
Some(2).and(Some(3)) # => Some(3)


272
273
274
275
276
# File 'lib/errgonomic/option.rb', line 272

def and(other)
  return self if none?

  other
end

#and_then(&block) ⇒ Object

If self is Some, call the given block with the inner value and return its result. Block must return an Option.

Examples:

None().and_then { |x| Some(x + 1) } # => None()
Some(2).and_then { |x| Some(x + 1) } # => Some(3)


284
285
286
287
288
289
290
291
292
293
# File 'lib/errgonomic/option.rb', line 284

def and_then(&block)
  return self if none?

  val = block.call(value)
  if !Errgonomic.give_me_ambiguous_downstream_errors? && !val.is_a?(Errgonomic::Option::Any)
    raise Errgonomic::ArgumentError.new, "block must return an Option, was #{val.class.name}"
  end

  val
end

#deconstructObject

Examples:

measurement = Errgonomic::Option::Some.new(1)
case measurement
in Errgonomic::Option::Some, value
  "Measurement is #{measurement.value}"
in Errgonomic::Option::None
  "Measurement is not available"
else
  "not matched"
end # => "Measurement is 1"


47
48
49
50
51
# File 'lib/errgonomic/option.rb', line 47

def deconstruct
  return [self, value] if some?

  [Errgonomic::Option::None]
end

#expect!(msg) ⇒ Object

returns the inner value if pressent, else raises an error with the given message

Examples:

Some(1).expect!("msg") # => 1
None().expect!("here's why this failed") # => raise Errgonomic::ExpectError, "here's why this failed"

Raises:



106
107
108
109
110
# File 'lib/errgonomic/option.rb', line 106

def expect!(msg)
  raise Errgonomic::ExpectError, msg if none?

  value
end

#map(&block) ⇒ Object

Maps the Option to another Option by applying a function to the contained value (if Some) or returns None. Raises a pedantic exception if the return value of the block is not an Option.

Examples:

Some(1).map { |x| x + 1 } # => Some(2)
None().map { |x| x + 1 } # => None()


167
168
169
170
171
# File 'lib/errgonomic/option.rb', line 167

def map(&block)
  return self if none?

  Some(block.call(value))
end

#map_or(default, &block) ⇒ Object

Returns the provided default (if none), or applies a function to the contained value (if some). If you want lazy evaluation for the provided value, use map_or_else.

Examples:

None().map_or(1) { 100 } # => Some(1)
Some(1).map_or(100) { |x| x + 1 } # => Some(2)
Some("foo").map_or(0) { |str| str.length } # => Some(3)


181
182
183
184
185
# File 'lib/errgonomic/option.rb', line 181

def map_or(default, &block)
  return Some(default) if none?

  Some(block.call(value))
end

#map_or_else(proc, &block) ⇒ Object

Computes a default from the given Proc if None, or applies the block to the contained value (if Some).

Examples:

None().map_or_else(-> { :foo }) { :bar } # => Some(:foo)
Some("str").map_or_else(-> { 100 }) { |str| str.length } # => Some(3)
None().map_or_else( -> { nil }) { |str| str.length } # => None()


194
195
196
197
198
199
200
201
# File 'lib/errgonomic/option.rb', line 194

def map_or_else(proc, &block)
  if none?
    val = proc.call
    return val ? Some(val) : None()
  end

  Some(block.call(value))
end

#none_or(&block) ⇒ Object Also known as: none_or?

return true if the contained value is None or the block returns truthy

Examples:

None().none_or { false } # => true
Some(1).none_or { |x| x > 0 } # => true
Some(1).none_or { |x| x < 0 } # => false


73
74
75
76
77
# File 'lib/errgonomic/option.rb', line 73

def none_or(&block)
  return true if none?

  !!block.call(value)
end

#okObject

convert the option into a result where Some is Ok and None is Err

Examples:

None().ok # => Err()
Some(1).ok # => Ok(1)


207
208
209
210
211
# File 'lib/errgonomic/option.rb', line 207

def ok
  return Errgonomic::Result::Ok.new(value) if some?

  Errgonomic::Result::Err.new
end

#ok_or(err) ⇒ Object

Transforms the option into a result, mapping Some(v) to Ok(v) and None to Err(err)

Examples:

None().ok_or("wow") # => Err("wow")
Some(1).ok_or("such err") # => Ok(1)


218
219
220
221
222
# File 'lib/errgonomic/option.rb', line 218

def ok_or(err)
  return Errgonomic::Result::Ok.new(value) if some?

  Errgonomic::Result::Err.new(err)
end

#ok_or_else(&block) ⇒ Object

Transforms the option into a result, mapping Some(v) to Ok(v) and None to Err(err). TODO: block or proc?

Examples:

None().ok_or_else { "wow" } # => Err("wow")
Some("foo").ok_or_else { "such err" } # => Ok("foo")


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

def ok_or_else(&block)
  return Errgonomic::Result::Ok.new(value) if some?

  Errgonomic::Result::Err.new(block.call)
end

#or(other) ⇒ Object

Returns the option if it contains a value, otherwise returns the provided Option. Returns an Option.

Examples:

None().or(Some(1)) # => Some(1)
Some(2).or(Some(3)) # => Some(2)
None().or(2) # => raise Errgonomic::ArgumentError.new, "other must be an Option, was Integer"

Raises:



242
243
244
245
246
247
248
# File 'lib/errgonomic/option.rb', line 242

def or(other)
  raise ArgumentError, "other must be an Option, was #{other.class.name}" unless other.is_a?(Any)

  return self if some?

  other
end

#or_else(&block) ⇒ Object

Returns the option if it contains a value, otherwise calls the block and returns the result. Returns an Option.

Examples:

None().or_else { Some(1) } # => Some(1)
Some(2).or_else { Some(3) } # => Some(2)
None().or_else { 2 } # => raise Errgonomic::ArgumentError.new, "block must return an Option, was Integer"


256
257
258
259
260
261
262
263
264
265
# File 'lib/errgonomic/option.rb', line 256

def or_else(&block)
  return self if some?

  val = block.call
  if !val.is_a?(Errgonomic::Option::Any) && !Errgonomic.give_me_ambiguous_downstream_errors?
    raise Errgonomic::ArgumentError.new, "block must return an Option, was #{val.class.name}"
  end

  val
end

#some_and(&block) ⇒ Object Also known as: some_and?

return true if the contained value is Some and the block returns truthy

Examples:

Some(1).some_and { |x| x > 0 } # => true
Some(0).some_and { |x| x > 0 } # => false
None().some_and { |x| x > 0 } # => false


59
60
61
62
63
# File 'lib/errgonomic/option.rb', line 59

def some_and(&block)
  return false if none?

  !!block.call(value)
end

#tap_some(&block) ⇒ Object

Calls a function with the inner value, if Some, but returns the original option. In Rust, this is “inspect” but that clashes with Ruby conventions. We call this “tap_some” to avoid further clashing with “tap.”

Examples:

tapped = false
Some(1).tap_some { |x| tapped = x } # => Some(1)
tapped # => 1
tapped = false
None().tap_some { tapped = true } # => None()
tapped # => false


155
156
157
158
# File 'lib/errgonomic/option.rb', line 155

def tap_some(&block)
  block.call(value) if some?
  self
end

#to_aObject

return an Array with the contained value, if any

Examples:

Some(1).to_a # => [1]
None().to_a # => []


85
86
87
88
89
# File 'lib/errgonomic/option.rb', line 85

def to_a
  return [] if none?

  [value]
end

#to_json(*_args) ⇒ Object

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

Examples:

None().to_json # => raise Errgonomic::SerializeError, "cannot serialize an unwrapped Option"

Raises:



341
342
343
# File 'lib/errgonomic/option.rb', line 341

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

#to_sObject

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

Examples:

None().to_s # => raise Errgonomic::SerializeError, "cannot serialize an unwrapped Option"

Raises:



330
331
332
# File 'lib/errgonomic/option.rb', line 330

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

#unwrap!Object

returns the inner value if present, else raises an error

Examples:

Some(1).unwrap! # => 1
None().unwrap! # => raise Errgonomic::UnwrapError, "cannot unwrap None"

Raises:



95
96
97
98
99
# File 'lib/errgonomic/option.rb', line 95

def unwrap!
  raise Errgonomic::UnwrapError, 'cannot unwrap None' if none?

  value
end

#unwrap_or(default) ⇒ Object

returns the inner value if present, else returns the default value

Examples:

Some(1).unwrap_or(2) # => 1
None().unwrap_or(2) # => 2


116
117
118
119
120
# File 'lib/errgonomic/option.rb', line 116

def unwrap_or(default)
  return default if none?

  value
end

#unwrap_or_else(&block) ⇒ Object

returns the inner value if present, else returns the result of the provided block

Examples:

Some(1).unwrap_or_else { 2 } # => 1
None().unwrap_or_else { 2 } # => 2


137
138
139
140
141
# File 'lib/errgonomic/option.rb', line 137

def unwrap_or_else(&block)
  return block.call if none?

  value
end

#zip(other) ⇒ Object

Zips self with another Option.

If self is Some(s) and other is Some(o), this method returns Some([s, o]). Otherwise, None is returned.

Examples:

None().zip(Some(1)) # => None()
Some(1).zip(None()) # => None()
Some(2).zip(Some(3)) # => Some([2, 3])


304
305
306
307
308
# File 'lib/errgonomic/option.rb', line 304

def zip(other)
  return None() unless some? && other.some?

  Some([value, other.value])
end

#zip_with(other, &block) ⇒ Object

Zip two options using the block passed. If self is Some and Other is some, yield both of their values to the block and return its value as Some. Else return None.

Examples:

None().zip_with(Some(1)) { |a, b| a + b } # => None()
Some(1).zip_with(None()) { |a, b| a + b } # => None()
Some(2).zip_with(Some(3)) { |a, b| a + b } # => Some(5)


318
319
320
321
322
323
# File 'lib/errgonomic/option.rb', line 318

def zip_with(other, &block)
  return None() unless some? && other.some?

  other = block.call(value, other.value)
  Some(other)
end