Class: Errgonomic::Option::Any
- Includes:
- Comparable
- Defined in:
- lib/errgonomic/option.rb,
lib/errgonomic/rails/active_record_optional.rb
Overview
An Option is already lifted. Lifting it again would nest it, and the nesting is invisible until something reaches for the inner value.
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.
-
#as_json(*_args) ⇒ Object
ActiveSupport's Hash#as_json and Array#as_json recurse through their members with as_json rather than to_json, so an Option nested in a payload reaches Object#as_json and serializes as its instance variables.
- #blank? ⇒ Boolean
- #blank_or(_default) ⇒ Object
- #blank_or_else(&_block) ⇒ Object
- #blank_or_raise!(_message) ⇒ Object (also: #blank_or_raise)
- #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.
-
#presence ⇒ Object?
Returns the inner value of a Some, and nil on a None, so the Rails
presence || defaultidiom reaches the value rather than the wrapper. -
#present? ⇒ Boolean
Presence follows the discriminant, not the inner value: Some is present, None is blank.
-
#present_or(default) ⇒ Object
Returns the inner value of a Some, and the given default on a None.
-
#present_or_else(&block) ⇒ Object
Returns the inner value of a Some, and the result of the block on a None.
-
#present_or_raise!(message) ⇒ Object
(also: #present_or_raise)
Returns the inner value of a Some, and raises on a None.
-
#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_option ⇒ Object
-
#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.
486 487 488 489 490 |
# File 'lib/errgonomic/option.rb', line 486 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.
498 499 500 501 502 503 504 505 506 507 |
# File 'lib/errgonomic/option.rb', line 498 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 |
#as_json(*_args) ⇒ Object
ActiveSupport's Hash#as_json and Array#as_json recurse through their members with as_json rather than to_json, so an Option nested in a payload reaches Object#as_json and serializes as its instance variables. Refuse there too, and the guard holds wherever an Option travels.
564 565 566 |
# File 'lib/errgonomic/option.rb', line 564 def as_json(*_args) raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Option' end |
#blank? ⇒ Boolean
193 194 195 |
# File 'lib/errgonomic/option.rb', line 193 def blank? none? end |
#blank_or(_default) ⇒ Object
279 280 281 |
# File 'lib/errgonomic/option.rb', line 279 def blank_or(_default) raise_blank_side_teaching(:blank_or) end |
#blank_or_else(&_block) ⇒ Object
289 290 291 |
# File 'lib/errgonomic/option.rb', line 289 def blank_or_else(&_block) raise_blank_side_teaching(:blank_or_else) end |
#blank_or_raise!(_message) ⇒ Object Also known as: blank_or_raise
299 300 301 |
# File 'lib/errgonomic/option.rb', line 299 def blank_or_raise!() raise_blank_side_teaching(:blank_or_raise!) 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
330 331 332 333 334 |
# File 'lib/errgonomic/option.rb', line 330 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.
581 582 583 584 585 |
# File 'lib/errgonomic/option.rb', line 581 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.
596 597 598 599 600 601 602 603 604 605 |
# File 'lib/errgonomic/option.rb', line 596 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.
381 382 383 384 385 |
# File 'lib/errgonomic/option.rb', line 381 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.
395 396 397 398 399 |
# File 'lib/errgonomic/option.rb', line 395 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).
408 409 410 411 412 413 414 415 |
# File 'lib/errgonomic/option.rb', line 408 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
421 422 423 424 425 |
# File 'lib/errgonomic/option.rb', line 421 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)
432 433 434 435 436 |
# File 'lib/errgonomic/option.rb', line 432 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?
444 445 446 447 448 |
# File 'lib/errgonomic/option.rb', line 444 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.
456 457 458 459 460 461 462 |
# File 'lib/errgonomic/option.rb', line 456 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.
470 471 472 473 474 475 476 477 478 479 |
# File 'lib/errgonomic/option.rb', line 470 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 |
#presence ⇒ Object?
Returns the inner value of a Some, and nil on a None, so the Rails
presence || default idiom reaches the value rather than the wrapper.
266 267 268 269 270 271 |
# File 'lib/errgonomic/option.rb', line 266 def presence presence_nudge('presence', 'unwrap_or(nil)') return nil if none? value 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 |
#present_or(default) ⇒ Object
Returns the inner value of a Some, and the given default on a None. No pedantic type check on the default: this family is deprecated on Options, and unwrap_or, which the nudge points to, has none either.
234 235 236 237 238 239 |
# File 'lib/errgonomic/option.rb', line 234 def present_or(default) presence_nudge('present_or', 'unwrap_or') return default if none? value end |
#present_or_else(&block) ⇒ Object
Returns the inner value of a Some, and the result of the block on a None.
250 251 252 253 254 255 |
# File 'lib/errgonomic/option.rb', line 250 def present_or_else(&block) presence_nudge('present_or_else', 'unwrap_or_else') return block.call if none? value end |
#present_or_raise!(message) ⇒ Object Also known as: present_or_raise
Returns the inner value of a Some, and raises on a None. Presence follows the discriminant, so Some(nil) yields nil.
215 216 217 218 219 220 |
# File 'lib/errgonomic/option.rb', line 215 def present_or_raise!() presence_nudge('present_or_raise', 'expect!') raise Errgonomic::NotPresentError, if none? value end |
#pretty_print(pp) ⇒ Object
pp uses its own object dump unless told otherwise; keep it consistent with inspect.
570 571 572 |
# File 'lib/errgonomic/option.rb', line 570 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."
369 370 371 372 |
# File 'lib/errgonomic/option.rb', line 369 def tap_some(&block) block.call(value) if some? self end |
#to_a ⇒ Object
return an Array with the contained value, if any
309 310 311 312 313 |
# File 'lib/errgonomic/option.rb', line 309 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.
555 556 557 |
# File 'lib/errgonomic/option.rb', line 555 def to_json(*_args) raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Option' end |
#to_option ⇒ Object
265 266 267 |
# File 'lib/errgonomic/rails/active_record_optional.rb', line 265 def to_option self end |
#to_s ⇒ Object
Refuse to serialize an unwrapped Option as a String. Options must be correctly handled to access their inner value.
544 545 546 |
# File 'lib/errgonomic/option.rb', line 544 def to_s raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Option' end |
#unwrap! ⇒ Object
returns the inner value if present, else raises an error
319 320 321 322 323 |
# File 'lib/errgonomic/option.rb', line 319 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
340 341 342 343 344 |
# File 'lib/errgonomic/option.rb', line 340 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
351 352 353 354 355 |
# File 'lib/errgonomic/option.rb', line 351 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.
615 616 617 618 619 620 |
# File 'lib/errgonomic/option.rb', line 615 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.
518 519 520 521 522 |
# File 'lib/errgonomic/option.rb', line 518 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.
532 533 534 535 536 537 |
# File 'lib/errgonomic/option.rb', line 532 def zip_with(other, &block) return None() unless some? && other.some? other = block.call(value, other.value) Some(other) end |