Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

class ShopifyCli::Result::Success

Kevin O'Sullivan edited this page Jun 28, 2021 · 3 revisions

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.

Attributes

  • value

Class Methods

new

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
end

Instance Methods

success?

success?() always returns true to indicate that this result represents a success.

see source

# File lib/shopify-cli/result.rb, line 95
def success?
  true
end

failure?

failure?() always returns false to indicate that this result represents a success.

see source

# File lib/shopify-cli/result.rb, line 102
def failure?
  false
end

error

error() 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
end

map

map(&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
end

then

then(&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)
end

rescue

rescue() 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
end

unwrap

unwrap(*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

Clone this wiki locally