Class: ShopifyCLI::Result::Failure

Inherits:
Object
  • Object
show all
Defined in:
lib/shopify_cli/result.rb

Overview

Implements a container for wrapping an error value. In many cases, the error value is going to be an exception but other values are fully supported:

Failure
  .new(RuntimeError.new("Something went wrong"))
  .error # => RuntimeError.new

Failure
  .new("Something went wrong")
  .error # => "Something went wrong"

‘Failure` does not support transformations with `then` and `map`. When any of these two methods is invoked on a `Failure`, the `Failure` itself is returned unless it is rescued from or unwrapped. This enables the caller to build optimistic transformation chains and defer error handling:

Failure
  .new(nil)
  .then { |json| JSON.parse(json) }                      # Ignored
  .then(&:with_indifferent_access)                       # Ignored
  .then { |data| data.values_at(:firstname, :lastname) } # Ignored
  .unwrap(Person.new("John", "Doe"))                     # => Person

Alternatively, we could rescue from the error and then proceed with the remaining transformations:

Person = Struct.new(:firstname, :lastname)
Failure
  .new(nil)
  .then { |json| JSON.parse(json) }                      # Ignored
  .then(&:with_indifferent_access)                       # Ignored
  .rescue { {firstname: "John", lastname: "Doe" }}
  .then { |data| data.values_at(:firstname, :lastname) } # Executed
  .then { |members| Person.new(*members) }               # Executed
  .unwrap(nil)                                           # => Person

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(error) ⇒ Failure

initializes a new ‘Failure` from an arbitrary value. In many cases, this value is going to be an instance of a subclass of `Exception` but any type is supported.



252
253
254
# File 'lib/shopify_cli/result.rb', line 252

def initialize(error)
  @error = error
end

Instance Attribute Details

#errorObject (readonly)

Returns the value of attribute error.



245
246
247
# File 'lib/shopify_cli/result.rb', line 245

def error
  @error
end

Instance Method Details

#failure?Boolean

Always returns ‘true` to indicate that this result represents a failure.

Returns:

  • (Boolean)


266
267
268
# File 'lib/shopify_cli/result.rb', line 266

def failure?
  true
end

#mapObject

is a no-op and simply returns itself. This is essential to skip transformation steps in a chain once an error has occurred.



282
283
284
# File 'lib/shopify_cli/result.rb', line 282

def map
  self
end

#rescue(&block) ⇒ Object

can be used to recover from a failure or produce a new failure with a different error.

Failure
  .new("Something went wrong")
  .rescue { |msg| [msg, "but we fixed it!"].join(" "") }
  .tap do |result|
     result.success? # => true
     result.value # => "Something went wrong but we fixed it!"
  end

‘rescue` is opinionated when it comes to the return value of the block. If the return value is an `Exception` – either one that was raised or an instance of a subclass of `Exception` – a `Failure` is returned. Any other value results in a `Success` unless the value has been explicitly wrapped in a `Failure`:

Failure
  .new(RuntimeError.new)
  .rescue { "All good! "}
  .success? # => true

Failure
  .new(RuntimeError.new)
  .rescue { Failure.new("Still broken!") }
  .success? # => false


322
323
324
# File 'lib/shopify_cli/result.rb', line 322

def rescue(&block)
  Result.wrap(&block).call(@error)
end

#success?Boolean

always returns ‘false` to indicate that this result represents a failure.

Returns:

  • (Boolean)


259
260
261
# File 'lib/shopify_cli/result.rb', line 259

def success?
  false
end

#thenObject

is a no-op and simply returns itself. This is essential to skip transformation steps in a chain once an error has occurred.



290
291
292
# File 'lib/shopify_cli/result.rb', line 290

def then
  self
end

#unwrap(*args, &block) ⇒ Object

returns the fallback value specified by the caller. The fallback value can be provided as a method argument or as a block. If a block is given, it receives the error as its first and only argument:

failure = Failure.new(RuntimeError.new("Something went wrong!"))

failure.unwrap(nil) # => nil
failure.unwrap { |e| e.message } # => "Something went wrong!"

#### Parameters

  • ‘*args` should be an `Array` with zero or one element

  • ‘&block` should be a Proc that takes zero or one argument

#### Raises

  • ‘ArgumentError` if both a fallback argument and a block is provided

Raises:

  • (ArgumentError)


345
346
347
348
# File 'lib/shopify_cli/result.rb', line 345

def unwrap(*args, &block)
  raise ArgumentError, "expected either a fallback value or a block" unless (args.length == 1) ^ block
  block ? block.call(@error) : args.pop
end

#unwrap!Object

raises the error this failure represents



353
354
355
# File 'lib/shopify_cli/result.rb', line 353

def unwrap!
  raise error
end

#valueObject

raises an ‘ShopifyCLI::Result::UnexpectedError` as a `ShopifyCLI::Result::Failure` does not carry a success value.

Raises:



274
275
276
# File 'lib/shopify_cli/result.rb', line 274

def value
  raise UnexpectedFailure
end