diff --git a/lib/errgonomic.rb b/lib/errgonomic.rb index 4251eb3..27f253f 100644 --- a/lib/errgonomic.rb +++ b/lib/errgonomic.rb @@ -30,7 +30,9 @@ class NotPresentError < Error; end class TypeMismatchError < Error; end class UnwrapError < Error - def initialize(msg, value) + attr_reader :value + + def initialize(msg, value = nil) super(msg) @value = value end @@ -47,8 +49,9 @@ class NotComparableError < StandardError; end class SerializeError < TypeError; end # A little bit of control over how pedantic we are in our runtime type checks. + # Default is false: we are pedantic and raise errors on type mismatches. def self.give_me_ambiguous_downstream_errors? - @give_me_ambiguous_downstream_errors || true + !!@give_me_ambiguous_downstream_errors end # You can opt out of the pedantic runtime checks for lazy block evaluation, diff --git a/lib/errgonomic/option.rb b/lib/errgonomic/option.rb index eb76830..4b81bf6 100644 --- a/lib/errgonomic/option.rb +++ b/lib/errgonomic/option.rb @@ -257,7 +257,7 @@ def or_else(&block) return self if some? val = block.call - if !val.is_a?(Errgonomic::Option::Any) && Errgonomic.give_me_ambiguous_downstream_errors? + 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 @@ -275,16 +275,17 @@ def and(other) other end - # If self is Some, call the given block and return its value. Block most return an Option. + # If self is Some, call the given block with the inner value and return + # its result. Block must return an Option. # # @example - # None().and_then { Some(1) } # => None() - # Some(2).and_then { Some(3) } # => Some(3) + # None().and_then { |x| Some(x + 1) } # => None() + # Some(2).and_then { |x| Some(x + 1) } # => Some(3) def and_then(&block) return self if none? - val = block.call - if Errgonomic.give_me_ambiguous_downstream_errors? && !val.is_a?(Errgonomic::Option::Any) + 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 diff --git a/lib/errgonomic/result.rb b/lib/errgonomic/result.rb index 0478c2d..97df1e7 100644 --- a/lib/errgonomic/result.rb +++ b/lib/errgonomic/result.rb @@ -142,7 +142,7 @@ 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? + if !res.is_a?(Errgonomic::Result::Any) && !Errgonomic.give_me_ambiguous_downstream_errors? raise Errgonomic::ArgumentError, 'and_then block must return a Result' end @@ -173,20 +173,18 @@ def or(other) # Sorry about that, hopefully it helps your tests. Better than ambiguous # downstream "undefined method" errors, probably. # - # TODO yield the Err - # # @param block [Proc] # # @example - # Ok(1).or_else { Ok(2) } # => Ok(1) - # Err(:o).or_else { Ok(1) } # => Ok(1) - # Err(:q).or_else { Err(:r) } # => Err(:r) - # Err(:s).or_else { "oops" } # => raise Errgonomic::ArgumentError, "or_else block must return a Result" + # 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" def or_else(&block) return self if ok? - res = block.call(self) - if !res.is_a?(Errgonomic::Result::Any) && Errgonomic.give_me_ambiguous_downstream_errors? + 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 @@ -249,8 +247,7 @@ def tap_ok(&block) def map(&block) return self if err? - @value = block.call(value) - self + Ok(block.call(value)) end # Refuse to serialize an unwrapped Result as a String. Results must be