-
Notifications
You must be signed in to change notification settings - Fork 213
class ShopifyCli::Result::Success
Implements a container for wrapping a success value. The main purpose of the container is to support further transformations of the result and centralize error handling should any of the subsequent transformations fail:
result = Result
.new("{}")
.then { |json| JSON.parse(json) }
.tap do |result|
result.success? # => true
result.value # => {}
.then { |data| data.fetch(:firstname) }
.tap do |result|
result.failure? # => true
result.error # => KeyError
end
Success implements two transformation functions: then and map. The
former makes no assumption regarding the return value of the transformation.
The latter on the other hand expects the transformation to be successful. If
this assumption is violated, program execution is interrupted and an error is
raised. As the purpose of result objects is to guard against exactly that.
This is generally a flaw and requires the code to either be hardened or to
substitute the call to map with a call to then. map should only be used
for transformations that cannot fail and when the caller wants to state
exactly that fact.
value
new(value)
initializes a new Success from an arbitrary value.
see source
# File lib/shopify-cli/result.rb, line 88
def initialize(value)
@value = value
endsuccess?()
always returns true to indicate that this result represents a success.
see source
# File lib/shopify-cli/result.rb, line 95
def success?
true
endfailure?()
always returns false to indicate that this result represents a success.
see source
# File lib/shopify-cli/result.rb, line 102
def failure?
false
enderror()
raises an UnexpectedSuccess as a Success does not carry an error value.
see source
# File lib/shopify-cli/result.rb, line 110
def error
raise UnexpectedSuccess
endmap(&block)
returns a new Success wrapping the result of the given block. The block is
called with the current value. If the block raises an exception or returns a
Failure, an exception is raised. map assumes any transformation to
succeed. Transformations that are expected to fail under certain conditions
should only be transformed using then:
Success
.new(nil)
.map { |n| n + 1 } # => raises NoMethodError
Therefore, map should only be used here if the previous success value is guaranteed to be a number or if the block handles nil cases properly:
Success
.new(nil)
.map { |n| (n || 0) + 1 }
.value # => 1
see source
# File lib/shopify-cli/result.rb, line 133
def map(&block)
self.then(&block).tap do |result|
return result if result.success?
result.unwrap { |error| error }.tap do |error|
case error
when Exception
raise error
else
raise UnexpectedFailure, error
end
end
end
endthen(&block)
returns a new result by wrapping the return value of the block. The block is
invoked with the current success value. The result can either be a Success
or a Failure. The former is the default. The latter occurs when executing
the block either
- raised an exception,
- returned an instance of a subclass of
Exception, or - returned a
Failure.
The example below illustrates this behavior:
result = Success
.new(1)
.then { |n| n + 1 }
.tap do |result|
result.success? # => true
result.value # => 2
end
result.then { |n| n / 0 }.error # => ZeroDivisionError
result.then { RuntimeError.new }.error # => RuntimeError
result.then { Failure.new("Boom!") }.error # => "Boom!"
see source
# File lib/shopify-cli/result.rb, line 172
def then(&block)
Result.wrap(&block).call(@value)
endrescue()
is a no-op and simply returns itself. Only a Failure can be transformed
using rescue.
see source
# File lib/shopify-cli/result.rb, line 180
def rescue
self
endunwrap(*args, &block)
returns the success value and ignores the fallback value that was either
provided as a method argument or by passing a block. However, the caller is
still required to specify a fallback value to ensure that in the event of a
Failure program execution can continue in a controlled manner:
Success.new(1).unwrap(0) => 1
see source
# File lib/shopify-cli/result.rb, line 193
def unwrap(*args, &block)
raise ArgumentError, "expected either a fallback value or a block" unless (args.length == 1) ^ block
@value
end