Class: Errgonomic::Option::Any
- Includes:
- Comparable
- Defined in:
- lib/errgonomic/option.rb
Overview
The base class for all options. Some and None are subclasses.
Constant Summary collapse
- RUST_SPELLINGS =
Rust spellings we accept but do not advertise: they delegate to the Ruby-idiomatic predicate and nudge the caller there via stderr.
{ is_some: :some?, is_none: :none?, is_some_and: :some_and?, is_none_or: :none_or? }.freeze
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Options order like Rust's: None sorts before any Some, and Somes order by their inner values.
-
#==(other) ⇒ Object
An Option equals another Option of the same class with an equal inner value.
-
#and(other) ⇒ Object
If self is Some, return the provided other Option.
-
#and_then(&block) ⇒ Object
If self is Some, call the given block with the inner value and return its result.
- #blank? ⇒ Boolean
- #deconstruct ⇒ Object
-
#eql?(other) ⇒ Boolean
Hash-based collections (Hash keys, Set, uniq, group_by) use eql? and hash, not ==.
-
#expect!(msg) ⇒ Object
returns the inner value if pressent, else raises an error with the given message.
-
#filter(&block) ⇒ Object
Return self if the predicate is truthy for the inner value, else None.
-
#flatten ⇒ Object
Remove one level of Option nesting.
- #hash ⇒ Object
-
#map(&block) ⇒ Object
Maps the Option to another Option by applying a function to the contained value (if Some) or returns None.
-
#map_or(default, &block) ⇒ Object
Returns the provided default (if none), or applies a function to the contained value (if some).
-
#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).
-
#method_missing(name, *args, &block) ⇒ Object
An Option deliberately forwards nothing to its inner value, so a miss here is almost always someone treating the container as its contents.
-
#none_or(&block) ⇒ Object
(also: #none_or?)
return true if the contained value is None or the block returns truthy.
-
#ok ⇒ Object
convert the option into a result where Some is Ok and None is Err.
-
#ok_or(err) ⇒ Object
Transforms the option into a result, mapping Some(v) to Ok(v) and None to Err(err).
-
#ok_or_else(&block) ⇒ Object
Transforms the option into a result, mapping Some(v) to Ok(v) and None to Err(err).
-
#or(other) ⇒ Object
Returns the option if it contains a value, otherwise returns the provided Option.
-
#or_else(&block) ⇒ Object
Returns the option if it contains a value, otherwise calls the block and returns the result.
-
#present? ⇒ Boolean
Presence follows the discriminant, not the inner value: Some is present, None is blank.
-
#pretty_print(pp) ⇒ Object
pp uses its own object dump unless told otherwise; keep it consistent with inspect.
- #respond_to_missing?(name, include_private = false) ⇒ Boolean
-
#some_and(&block) ⇒ Object
(also: #some_and?)
return true if the contained value is Some and the block returns truthy.
-
#tap_some(&block) ⇒ Object
Calls a function with the inner value, if Some, but returns the original option.
-
#to_a ⇒ Object
return an Array with the contained value, if any.
-
#to_json(*_args) ⇒ Object
Refuse to serialize an unwrapped Option as JSON.
-
#to_s ⇒ Object
Refuse to serialize an unwrapped Option as a String.
-
#unwrap! ⇒ Object
returns the inner value if present, else raises an error.
-
#unwrap_or(default) ⇒ Object
returns the inner value if present, else returns the default value.
-
#unwrap_or_else(&block) ⇒ Object
returns the inner value if present, else returns the result of the provided block.
-
#xor(other) ⇒ Object
Return Some when either self or other are Some, otherwise return None when both are None or both are Some.
-
#zip(other) ⇒ Object
Zips self with another Option.
-
#zip_with(other, &block) ⇒ Object
Zip two options using the block passed.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
An Option deliberately forwards nothing to its inner value, so a miss here is almost always someone treating the container as its contents. Teach the route out instead of leaving a bare NoMethodError. Rust spellings of the predicates delegate, with a nudge on stderr.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/errgonomic/option.rb', line 34 def method_missing(name, *args, &block) if (canonical = RUST_SPELLINGS[name]) warn "Errgonomic: `#{name}` is the Rust spelling; prefer `#{canonical}`. Delegating." return public_send(canonical, *args, &block) end raise Errgonomic::UnwrappedAccessError.new(<<~MSG, name) undefined method `#{name}' for #{inspect}, an Option, which does not forward methods to its inner value. Reach for a combinator instead: map, and_then, filter: transform the value if present unwrap_or, unwrap_or_else: supply a fallback ok_or, ok_or_else: convert to a Result some_and?, none_or?: test a predicate against the inner value unwrap! and expect! also exist, but are intended for tests rather than application code. MSG end |
Instance Method Details
#<=>(other) ⇒ Object
Options order like Rust's: None sorts before any Some, and Somes order by their inner values. Follows Ruby's <=> convention of returning nil for incomparable operands, whether the other object is not an Option or the inner values do not themselves compare.
140 141 142 143 144 145 146 |
# File 'lib/errgonomic/option.rb', line 140 def <=>(other) return nil unless other.is_a?(Errgonomic::Option::Any) return none? ? 0 : 1 if other.none? return -1 if none? value <=> other.value end |
#==(other) ⇒ Object
An Option equals another Option of the same class with an equal inner value. Anything else, including nil and the raw inner value, is not equal: quietly false, never an error. Rust rejects Some(5) == 5 at compile time; Ruby cannot, and raising here would break the many places Ruby compares heterogeneous operands (Array#include?, assertion diffs, dirty tracking). Compare Options (opt == Some(5)) or test the inner value (opt.some_and? { |v| v == 5 }) instead.
None() == nil is likewise false: None is a value that represents absence, not an absence Ruby can see. (The Rails integration separately makes None#nil? answer true, as an ActiveRecord compromise; equality does not follow it.)
75 76 77 78 79 80 |
# File 'lib/errgonomic/option.rb', line 75 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.
378 379 380 381 382 |
# File 'lib/errgonomic/option.rb', line 378 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.
390 391 392 393 394 395 396 397 398 399 |
# File 'lib/errgonomic/option.rb', line 390 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 |
#blank? ⇒ Boolean
193 194 195 |
# File 'lib/errgonomic/option.rb', line 193 def blank? none? end |
#deconstruct ⇒ Object
120 121 122 123 124 |
# File 'lib/errgonomic/option.rb', line 120 def deconstruct return [self, value] if some? [Errgonomic::Option::None] end |
#eql?(other) ⇒ Boolean
Hash-based collections (Hash keys, Set, uniq, group_by) use eql? and hash, not ==. Follow the inner value's own eql? semantics, so Options behave as keys exactly like their inner values: Some(1) and Some(1.0) are distinct keys, just as 1 and 1.0 are.
93 94 95 96 97 98 |
# File 'lib/errgonomic/option.rb', line 93 def eql?(other) return false if self.class != other.class return true if none? value.eql?(other.value) end |
#expect!(msg) ⇒ Object
returns the inner value if pressent, else raises an error with the given message
222 223 224 225 226 |
# File 'lib/errgonomic/option.rb', line 222 def expect!(msg) raise Errgonomic::ExpectError, msg if none? value end |
#filter(&block) ⇒ Object
Return self if the predicate is truthy for the inner value, else None. None passes through.
464 465 466 467 468 |
# File 'lib/errgonomic/option.rb', line 464 def filter(&block) return self if none? block.call(value) ? self : None() end |
#flatten ⇒ Object
Remove one level of Option nesting. Pedantically raises when the inner value is not itself an Option, which in Rust would not have compiled.
479 480 481 482 483 484 485 486 487 488 |
# File 'lib/errgonomic/option.rb', line 479 def flatten return self if none? unless value.is_a?(Errgonomic::Option::Any) raise Errgonomic::TypeMismatchError, "cannot flatten #{value.class}; it is not an Option" end value end |
#hash ⇒ Object
104 105 106 107 108 |
# File 'lib/errgonomic/option.rb', line 104 def hash return self.class.hash if none? [self.class, value].hash 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.
273 274 275 276 277 |
# File 'lib/errgonomic/option.rb', line 273 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.
287 288 289 290 291 |
# File 'lib/errgonomic/option.rb', line 287 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).
300 301 302 303 304 305 306 307 |
# File 'lib/errgonomic/option.rb', line 300 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
168 169 170 171 172 |
# File 'lib/errgonomic/option.rb', line 168 def none_or(&block) return true if none? !!block.call(value) end |
#ok ⇒ Object
convert the option into a result where Some is Ok and None is Err
313 314 315 316 317 |
# File 'lib/errgonomic/option.rb', line 313 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)
324 325 326 327 328 |
# File 'lib/errgonomic/option.rb', line 324 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?
336 337 338 339 340 |
# File 'lib/errgonomic/option.rb', line 336 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.
348 349 350 351 352 353 354 |
# File 'lib/errgonomic/option.rb', line 348 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.
362 363 364 365 366 367 368 369 370 371 |
# File 'lib/errgonomic/option.rb', line 362 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 |
#present? ⇒ Boolean
Presence follows the discriminant, not the inner value: Some is present, None is blank. So Some(false) and Some(nil) are present, unlike their unwrapped values.
185 186 187 |
# File 'lib/errgonomic/option.rb', line 185 def present? some? end |
#pretty_print(pp) ⇒ Object
pp uses its own object dump unless told otherwise; keep it consistent with inspect.
453 454 455 |
# File 'lib/errgonomic/option.rb', line 453 def pretty_print(pp) pp.text(inspect) end |
#respond_to_missing?(name, include_private = false) ⇒ Boolean
51 52 53 |
# File 'lib/errgonomic/option.rb', line 51 def respond_to_missing?(name, include_private = false) RUST_SPELLINGS.key?(name) || super end |
#some_and(&block) ⇒ Object Also known as: some_and?
return true if the contained value is Some and the block returns truthy
154 155 156 157 158 |
# File 'lib/errgonomic/option.rb', line 154 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."
261 262 263 264 |
# File 'lib/errgonomic/option.rb', line 261 def tap_some(&block) block.call(value) if some? self end |
#to_a ⇒ Object
return an Array with the contained value, if any
201 202 203 204 205 |
# File 'lib/errgonomic/option.rb', line 201 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.
447 448 449 |
# File 'lib/errgonomic/option.rb', line 447 def to_json(*_args) raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Option' end |
#to_s ⇒ Object
Refuse to serialize an unwrapped Option as a String. Options must be correctly handled to access their inner value.
436 437 438 |
# File 'lib/errgonomic/option.rb', line 436 def to_s raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Option' end |
#unwrap! ⇒ Object
returns the inner value if present, else raises an error
211 212 213 214 215 |
# File 'lib/errgonomic/option.rb', line 211 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
232 233 234 235 236 |
# File 'lib/errgonomic/option.rb', line 232 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
243 244 245 246 247 |
# File 'lib/errgonomic/option.rb', line 243 def unwrap_or_else(&block) return block.call if none? value end |
#xor(other) ⇒ Object
Return Some when either self or other are Some, otherwise return None when both are None or both are Some.
498 499 500 501 502 503 |
# File 'lib/errgonomic/option.rb', line 498 def xor(other) return self if some? && other.none? return other if other.some? && none? None() 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.
410 411 412 413 414 |
# File 'lib/errgonomic/option.rb', line 410 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.
424 425 426 427 428 429 |
# File 'lib/errgonomic/option.rb', line 424 def zip_with(other, &block) return None() unless some? && other.some? other = block.call(value, other.value) Some(other) end |