Class: Errgonomic::Result::Any
- Includes:
- Comparable
- 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.
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_ok: :ok?, is_err: :err?, is_ok_and: :ok_and?, is_err_and: :err_and? }.freeze
Instance Attribute Summary collapse
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Results order like Rust's: Ok sorts before any Err, and same variants order by their inner values.
-
#==(other) ⇒ Object
Equality comparison for Result objects is based on value not reference.
-
#and(other) ⇒ Object
Given another result, return it if the inner result is Ok, else return the inner Err.
-
#and_then(&block) ⇒ Object
Given a block, evaluate it and return its result if the inner result is Ok, else return the inner Err.
- #deconstruct ⇒ Object
-
#eql?(other) ⇒ Boolean
Hash-based collections (Hash keys, Set, uniq, group_by) use eql? and hash, not ==.
-
#err_and?(&block) ⇒ Boolean
Return true if the inner value is an Err and the result of the block is truthy.
-
#expect!(msg) ⇒ Object
Return the inner value of an Ok, else raise an exception with the given message when Err.
- #hash ⇒ Object
-
#initialize(value) ⇒ Any
constructor
A new instance of Any.
-
#map(&block) ⇒ Object
Map the Ok(a) to an Ok(b), preserving the Err.
-
#map_err(&block) ⇒ Object
Map the Err(e) to an Err(f), preserving the Ok.
-
#method_missing(name, *args, &block) ⇒ Object
A Result deliberately forwards nothing to its inner value, so a miss here is almost always someone treating the container as its contents.
-
#ok_and?(&block) ⇒ Boolean
Return true if the inner value is an Ok and the result of the block is truthy.
-
#or(other) ⇒ Object
Return other if self is Err, else return the original Option.
-
#or_else(&block) ⇒ Object
Return self if it is Ok, else lazy evaluate the block and return its result.
-
#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
-
#result? ⇒ Boolean
Indicate that this is some kind of result object.
-
#tap_err(&block) ⇒ Object
Calls the function with the inner error value, if Err, but returns the original Result.
-
#tap_ok(&block) ⇒ Object
Calls the function with the inner ok value, if Ok, while returning the original Result.
-
#to_json(*_args) ⇒ Object
Refuse to serialize an unwrapped Result as JSON.
-
#to_s ⇒ Object
Refuse to serialize an unwrapped Result as a String.
-
#unwrap! ⇒ Object
Return the inner value of an Ok, else raise an exception when Err.
-
#unwrap_err! ⇒ Object
Return the inner value of an Err, else raise an exception when Ok.
-
#unwrap_or(other) ⇒ Object
Return the inner value if self is Ok, else return the provided default.
-
#unwrap_or_else(&block) ⇒ Object
Return the inner value if self is Ok, else lazy evaluate the block and return its result.
Constructor Details
#initialize(value) ⇒ Any
Returns a new instance of Any.
13 14 15 |
# File 'lib/errgonomic/result.rb', line 13 def initialize(value) @value = value end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
A Result 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.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/errgonomic/result.rb', line 60 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}, a Result, which does not forward methods to its inner value. Reach for a combinator instead: map, map_err, and_then, or_else: transform the value or the error unwrap_or, unwrap_or_else: supply a fallback ok_and?, err_and?: test a predicate against the inner value unwrap!, unwrap_err!, and expect! also exist, but are intended for tests rather than application code. MSG end |
Instance Attribute Details
#value ⇒ Object (readonly)
Returns the value of attribute value.
11 12 13 |
# File 'lib/errgonomic/result.rb', line 11 def value @value end |
Instance Method Details
#<=>(other) ⇒ Object
Results order like Rust's: Ok sorts before any Err, and same variants order by their inner values. Follows Ruby's <=> convention of returning nil for incomparable operands, whether the other object is not a Result or the inner values do not themselves compare.
29 30 31 32 33 34 |
# File 'lib/errgonomic/result.rb', line 29 def <=>(other) return nil unless other.is_a?(Errgonomic::Result::Any) return ok? ? -1 : 1 if self.class != other.class value <=> other.value end |
#==(other) ⇒ Object
Equality comparison for Result objects is based on value not reference.
90 91 92 93 94 |
# File 'lib/errgonomic/result.rb', line 90 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.
207 208 209 210 211 212 |
# File 'lib/errgonomic/result.rb', line 207 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.
227 228 229 230 231 232 233 234 235 236 |
# File 'lib/errgonomic/result.rb', line 227 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 |
#deconstruct ⇒ Object
397 398 399 |
# File 'lib/errgonomic/result.rb', line 397 def deconstruct [self, value] 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 Results behave as keys exactly like their inner values.
106 107 108 |
# File 'lib/errgonomic/result.rb', line 106 def eql?(other) self.class == other.class && value.eql?(other.value) end |
#err_and?(&block) ⇒ Boolean
Return true if the inner value is an Err and the result of the block is truthy.
152 153 154 155 156 157 158 |
# File 'lib/errgonomic/result.rb', line 152 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.
179 180 181 182 183 |
# File 'lib/errgonomic/result.rb', line 179 def expect!(msg) raise Errgonomic::ExpectError, msg unless ok? @value end |
#hash ⇒ Object
113 114 115 |
# File 'lib/errgonomic/result.rb', line 113 def hash [self.class, value].hash end |
#map(&block) ⇒ Object
Map the Ok(a) to an Ok(b), preserving the Err
333 334 335 336 337 |
# File 'lib/errgonomic/result.rb', line 333 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
344 345 346 347 348 |
# File 'lib/errgonomic/result.rb', line 344 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.
138 139 140 141 142 |
# File 'lib/errgonomic/result.rb', line 138 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.
247 248 249 250 251 252 253 254 255 |
# File 'lib/errgonomic/result.rb', line 247 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.
269 270 271 272 273 274 275 276 277 278 |
# File 'lib/errgonomic/result.rb', line 269 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 |
#pretty_print(pp) ⇒ Object
pp uses its own object dump unless told otherwise; keep it consistent with inspect.
374 375 376 |
# File 'lib/errgonomic/result.rb', line 374 def pretty_print(pp) pp.text(inspect) end |
#respond_to_missing?(name, include_private = false) ⇒ Boolean
76 77 78 |
# File 'lib/errgonomic/result.rb', line 76 def respond_to_missing?(name, include_private = false) RUST_SPELLINGS.key?(name) || super end |
#result? ⇒ Boolean
Indicate that this is some kind of result object. Contrast to Object#result? which is false for all other types.
124 125 126 |
# File 'lib/errgonomic/result.rb', line 124 def result? true end |
#tap_err(&block) ⇒ Object
Calls the function with the inner error value, if Err, but returns the original Result.
316 317 318 319 |
# File 'lib/errgonomic/result.rb', line 316 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.
323 324 325 326 |
# File 'lib/errgonomic/result.rb', line 323 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.
368 369 370 |
# File 'lib/errgonomic/result.rb', line 368 def to_json(*_args) raise Errgonomic::SerializeError, 'cannot serialize an unwrapped Result' end |
#to_s ⇒ Object
Refuse to serialize an unwrapped Result as a String. Results must be correctly handled to access their inner value.
356 357 358 |
# File 'lib/errgonomic/result.rb', line 356 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.
165 166 167 168 169 |
# File 'lib/errgonomic/result.rb', line 165 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.
190 191 192 193 194 |
# File 'lib/errgonomic/result.rb', line 190 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.
287 288 289 290 291 |
# File 'lib/errgonomic/result.rb', line 287 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.
301 302 303 304 305 |
# File 'lib/errgonomic/result.rb', line 301 def unwrap_or_else(&block) return value if ok? block.call(value) end |