0.0
No release in over 3 years
Low commit activity in last 3 years
API for managing the results of operations.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.19.3
 Project Readme

MuchResult

API for managing the results of operations.

Usage

Have services/methods return a MuchResult based on the whether an exception was raised or not:

class PerformSomeOperation
  def self.call
    # Do something that could fail by raising an exception.
    MuchResult.success(message: "it worked!")
  rescue => exception
    MuchResult.failure(exception: exception)
  end
end

result = PerformSomeOperation.call

result.success?    # => true
result.failure?    # => false
result.message     # => "it worked!"
result.sub_results # => []

Have services/methods return a MuchResult based on a result value (i.e. "truthy" = success; "false-y" = failure):

def perform_some_operation(success:)
  # Do something that could fail.
  MuchResult.for(success, message: "it ran :shrug:")
end

result = perform_some_operation(success: true)
result.success? # => true
result.failure? # => false
result.message  # => "it ran :shrug:"

result = perform_some_operation(success: false)
result.success? # => false
result.failure? # => true
result.message  # => "it ran :shrug:"

result = perform_some_operation(success: nil)
result.success? # => false
result.failure? # => true
result.message  # => "it ran :shrug:"

Set arbitrary values on MuchResults before or after they are created:

result = MuchResult.success(message: "it worked!")
result.set(
  other_value1: "something else 1",
  other_value2: "something else 2"
)
result.message      # => "it worked!"
result.other_value1 # => "something else 1"
result.other_value2 # => "something else 2"

result.attribute_names # => [:message, :other_value1, :other_value2]

result.attributes
# => {
#  message: "it worked!",
#  other_value1: "something else 1",
#  other_value2: "something else 2"
# }

Capture sub-Results

Capture MuchResults for sub-operations into a parent MuchResult:

class PerformSomeOperation
  def self.call
    MuchResult.tap(description: "Do both parts") { |result|
      result          # => <MuchResult ...>
      result.success? # => true

      result.capture { do_part_1 }
      # OR you can use `capture_all` to capture from an Array of MuchResults

      # raise an Exception if failure
      result.capture! { do_part_2 }
      # OR you can use `capture_all!` to capture from an Array of MuchResults

      # set some arbitrary values b/c it worked.
      result.set(message: "it worked!")
    } # => result
  end

  def self.do_part_1
    MuchResult.failure(description: "Part 1")
  end

  def self.do_part_2
    MuchResult.success(description: "Part 2")
  end
end

result = PerformSomeOperation.call

result.success? # => true
result.message  # => "it worked!"

# Get just the immediate sub-results that were captured for the MuchResult.
result.sub_results # => [<MuchResult Part 1>, <MuchResult Part 2>]

# Get all MuchResults that make up this MuchResult (including those captured
# on all recursive sub-results).
result.all_results # => [result, <MuchResult Part 1>, <MuchResult Part 2>]

# Get aggregated values for each MuchResult that makes up this MuchResult.
result.get_for_sub_results(:description)
# => ["Part 1", "Part 2"]
result.get_for_success_sub_results(:description)
# => ["Part 2"]
result.get_for_failure_sub_results(:description)
# => ["Part 1"]

result.get_for_all_results(:description)
# => ["Do both parts", "Part 1", "Part 2"]
result.get_for_all_success_results(:description)
# => ["Part 2"]
result.get_for_all_failure_results(:description)
# => ["Do both parts", "Part 1"]

Transactions

Run transactions capturing sub-Results:

class PerformSomeOperation
  def self.call
    MuchResult.transaction(
      ActiveRecord::Base,
      value: "something",
      description: "Do both parts"
    ) { |transaction|
      transaction        # => <MuchResult::Transaction ...>
      transaction.result # => <MuchResult ...>

      # You can interact with a transaction as if it were a MuchResult.
      transaction.value    # => "something"
      transaction.success? # => true

      transaction.capture { do_part_1 }
      # OR you can use `capture_all` to capture from an Array of MuchResults

      # raise an Exception if failure (which will rollback the transaction)
      transaction.capture! { do_part_2 }
      # OR you can use `capture_all!` to capture from an Array of MuchResults

      # manually rollback the transaction if needed
      # (stops processing and doesn't commit the transaction)
      transaction.rollback if rollback_needed?

      # manually halt the transaction if needed
      # (stops processing and commits the transaction)
      transaction.halt if halt_needed?

      # set some arbitrary values b/c it worked.
      transaction.set(message: "it worked!")
    } # => transaction.result
  end

  def self.do_part_1
    MuchResult.failure(description: "Part 1")
  end

  def self.do_part_2
    MuchResult.success(description: "Part 2")
  end

  def rollback_needed?
    false
  end

  def halt_needed?
    false
  end
end

result = PerformSomeOperation.call

result.success? # => true
result.message  # => "it worked!"

result.much_result_transaction_rolled_back # => false
result.much_result_transaction_halted      # => false

# Get just the immediate sub-results that were captured for the MuchResult.
result.sub_results # => [<MuchResult Part 1>, <MuchResult Part 2>]

# Get all MuchResults that make up this MuchResult (including those captured
# on all recursive sub-results).
result.all_results # => [result, <MuchResult Part 1>, <MuchResult Part 2>]

# Get aggregated values for each MuchResult that makes up this MuchResult.
result.get_for_sub_results(:description)
# => ["Part 1", "Part 2"]
result.get_for_success_sub_results(:description)
# => ["Part 2"]
result.get_for_failure_sub_results(:description)
# => ["Part 1"]

result.get_for_all_results(:description)
# => ["Do both parts", "Part 1", "Part 2"]
result.get_for_all_success_results(:description)
# => ["Part 2"]
result.get_for_all_failure_results(:description)
# => ["Do both parts", "Part 1"]

Note: MuchResult::Transactions are designed to delegate to their MuchResult. You can interact with a MuchResult::Transaction as if it were a MuchResult.

You can configure a default transaction receiver (e.g. ActiveRecord::Base) in an initializer. Doing so means if no receiver is passed to MuchResult.transaction, the default receiver will be used:

# In an initializer or configuration script:
MuchResult.default_transaction_receiver = ActiveRecord::Base

# Since no receiver is passed, ActiveRecord::Base will be used:
MuchResult.transaction do |transaction|
  # ...
end

Installation

Add this line to your application's Gemfile:

gem "much-result"

And then execute:

$ bundle

Or install it yourself as:

$ gem install much-result

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am "Added some feature")
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request