RubyResult
Provides a very simple set of objects for dealing with return values in a more structured and consistent way.
include RubyResult
case result = Something.perform(*args)
when Success then do_something
when Failure then do_something_else
end
Installation
gem 'ruby_result'
And then execute:
$ bundle
Or install it yourself as:
$ gem install ruby_result
Usage
This gem provides two objects, RubyResult::Success
and RubyResult::Failure
. Each are initialized with one object, the value
. The value can be anything, though for consistency I usually return a Hash
. The gem is intended to help structure the handling of return values.
As an example, consider an ordering system where many things could fail when attempting to place an order. We'd want to wrap creating an order in a method or module but return an object that can represent success or failure and in each case provide some value.
module Orders
include RubyResult
def create_order(product, order_options:, customer_options:, shipping_address_options:, charge_options:)
result = nil
product.with_lock do
result = Failure(errors: ["Sold out"]) and raise ActiveRecord::Rollback if product.sold_out?
customer = Customer.find_or_create_by(customer_options)
result = Failure(errors: customer.errors.full_messages) and raise ActiveRecord::Rollback unless customer.valid?
address = Address.find_or_create_by(shipping_address_options.merge(customer: customer))
result = Failure(errors: address.errors.full_messages) and raise ActiveRecord::Rollback unless address.valid?
order = Orders::Order.create(order_options.merge(product: product, customer: customer))
result = Failure(errors: order.errors.full_messages) and raise ActiveRecord::Rollback unless order.valid?
charge = Orders::Charge.create(charge_options.merge(order: order))
result = Failure(errors: charge.errors.full_messages) and raise ActiveRecord::Rollback unless charge.valid?
result = Success(product: product, order: order, customer: customer, address: address, charge: charge)
end
result
end
end
Then we could call the above and handle the result in an Orders controller.
class OrdersController < ApplicationController
include RubyResult
def create
product = load_product(params[:product_id])
case result = Orders.create_order(product, order_options: order_options, customer_options: customer_options, shipping_address_options: shipping_address_options, charge_options: charge_options)
when Success
redirect_to customer_order_path(result.value[:customer], result.value[:order]), notice: "Successfully completed order"
when Failure
redirect_to product_path(product), alert: result.value[:errors].join(". ")
end
end
end
API
RubyResult#Success(value)
Convenience method for constructing a RubyResult::Success
object.
Success("hooray")
RubyResult#Failure(value)
Convenience method for constructing a RubyResult::Failure
object.
Failure("error message")
RubyResult::[Success,Failure].new(value)
Create a new Success
or Failure
object with some arbitrary value
.
RubyResult::[Success,Failure].===(other)
Compare other
with self
. If other
is an instance of self
, then it is true. Usefull in case statements.
case result = Something.perform(*args)
when Success then do_something
when Failure then do_something_else
end
RubyResult::[Success,Failure]#success?
Returns true if self
is an instance of RubyResult::Success
.
result = Something.perform(*args)
do_something if result.success?
RubyResult::[Success,Failure]#failure?
Returns true if self
is an instance of RubyResult::Failure
.
result = Something.perform(*args)
do_something if result.failure?
RubyResult::[Success,Failure]#value
Returns the value provided when constructing an Success
or Failure
object.
result = Success(order: order)
result.value # { order: <#Order...> }
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/benjreinhart/ruby_result. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.