class Flows::Result

@abstract

Result Object is a way of presenting the result of a calculation. The result may be successful or failed.

For example, if you calculate expression `a / b`:

Examples of such approach may be found in other libraries and languages:

So, why do you need Result Object? Why not just return `nil` on a failure or raise an error (like in the standard library)? Here are several reasons:

That's why `Flows` should have Result Object implementation. If any executable Flows entity will return Result Object with the same API - composing your app components becomes trivial. Result Objects should also be as fast and lightweight as possible.

Flows' implementation is inspired mainly by [Rust Result Type](doc.rust-lang.org/std/result/enum.Result.html) and focused on following features:

## General Recommendations

Let's assume that you have some code returning Result Object.

@example Creating Result Objects

# Successful result with data {a: 1}
x = Flows::Result::Ok.new(a: 1)

# Failure result with data {msg: 'error'}
x = Flows::Result::Err.new(msg: 'error')

# Successful result with data {a: 1} and status `:done`
x = Flows::Result::Ok.new({ a: 1 }, status: :done)

# Failure result with data {msg: 'error'} and status `:http_error`
x = Flows::Result::Err.new({ msg: 'error' }, status: :http_error)

# Successful result with data {a: 1} and metadata { time: 123 }
x = Flows::Result::Ok.new({ a: 1 }, meta: { time: 123 })

# Failure result with data {msg: 'error'} and metadata { time: 123 }
x = Flows::Result::Err.new({ msg: 'error' }, meta: { time: 123 })

@example Create Result Objects using helpers

class Demo
  # You cannot provide metadata using helpers and it's ok:
  # you shouldn't populate metadata in your business code.
  # Metadata is designed to use in library code and
  # when you have to provide some metadata from your library -
  # just use `.new` instead of helpers.
  include Flows::Result::Helpers

  def demo
    # Successful result with data {a: 1}
    x = ok(a: 1)

    # Failure result with data {msg: 'error'}
    x = err(msg: 'error')

    # Successful result with data {a: 1} and status `:done`
    x = ok(:done, a: 1)

    # Failure result with data {msg: 'error'} and status `:http_error`
    x = err(:http_error, msg: 'error')
  end
end

@example Inspecting Result Objects

# Behaviour of any result object:
result.status # returns status, example: `:ok`
result.meta # returns metadata, example: `{}`

# Behaviour specific to successful results:
result.ok? # true
result.err? # false
result.unwrap # returns result data
result.error # raises exception

# Behaviour specific to failure results:
result.ok? # false
result.err? # true
result.unwrap # raises exception
result.error # returns result data

@example Matching Results with case

case result
when Flows::Result::Ok then do_job
when Flows::Result::Err then give_up
end

@example Matching Results with case and helpers

class Demo
  include Flows::Result::Helpers

  def simple_usage
    case result
    when match_ok then do_job
    when match_err then give_up
    end
  end

  def with_status_matching
    case result
    when match_ok(:create) then do_create
    when match_ok(:update) then do_update
    when match_err(:http_error) then retry
    when match_err then give_up
    end
  end
end

@!method ok?

@abstract
@return [Boolean] `true` if result is successful

@!method err?

@abstract
@return [Boolean] `true` if result is failure

@!method unwrap

@abstract
@return [Object] result data
@raise [AccessError] if called on failure object

@!method error

@abstract
@return [Object] result data
@raise [AccessError] if called on successful object

@since 0.4.0

Attributes

data[RW]
meta[R]

@return [Hash] metadata, don't use it to store business data

status[R]

@return [Symbol] status of Result Object, default is `:ok` for successful results

and `:err` for failure results.

Public Class Methods

new(**) click to toggle source

Direct creation of this abstract class is forbidden.

@raise [StandardError] you will get an error

# File lib/flows/result.rb, line 177
def initialize(**)
  raise 'Use Flows::Result::Ok or Flows::Result::Err for build result objects'
end

Public Instance Methods

==(other) click to toggle source

Results are equal if have same type and data.

Metadata is ignored in comparison.

@return [Boolean]

# File lib/flows/result.rb, line 186
def ==(other)
  return false if self.class != other.class

  (status == other.status) && (data == other.send(:data))
end