🔃 Zx::Result
Functional result object for Ruby
Motivation
Because in sometimes, we need to create a safe return for our objects. This gem simplify this work.
Documentation <!-- omit in toc -->
Version | Documentation |
---|---|
unreleased | https://github.com/thadeu/zx-result/blob/main/README.md |
Table of Contents <!-- omit in toc -->
Compatibility
kind | branch | ruby |
---|---|---|
unreleased | main | >= 2.5.8, <= 3.1.x |
Installation
Use bundle
bundle add zx-result
or add this line to your application's Gemfile.
gem 'zx-result'
and then, require module
require 'zx'
Configuration
Without configuration, because we use only Ruby. ❤️
Usage
Success Type
result = Zx.Success(5)
result.success? #=> true
result.failure? #=> false
result.value #=> 5
result.value! #=> 5 or raise
result.error #=> nil or raises an exception
result = Zx.Success(5, type: :integer)
result.success? #=> true
result.failure? #=> false
result.value #=> 5
result.value! #=> 5 or raise
result.error #=> nil or raises an exception
result.type #=> :integer
Failure Type
result = Zx.Failure(:fizz)
result.success? #=> false
result.failure? #=> true
result.value #=> raises an exception
result.error #=> :fizz
result.type #=> :error
result = Zx.Failure(:fizz, type: :not_found)
result.success? #=> false
result.failure? #=> true
result.value #=> raises an exception
result.error #=> :fizz
result.type #=> :not_found
Map or Then
result = Zx.Success(5, type: :integer)
.fmap{ |number| number + 5 }
.fmap{ |number| number + 5 }
.fmap{ |number| number + 5 }
.on_success(:integer) {|number| puts number } #=> 20
.on(:success, :integer) {|number| puts number } #=> 20
.on_success {|number| puts number } #=> 20
result.success? #=> true
result.failure? #=> false
result.value #=> 20
result.value! #=> 20 or raise
result.error #=> nil or raises a n exception
result.type #=> :integer
result = Zx.Success(5, type: :integer)
.then{ |number| number + 5 }
.then{ |number| number + 5 }
.then{ |number| number + 5 }
.on_success{|number| puts number } #=> 20
result.success? #=> true
result.failure? #=> false
result.value #=> 20
result.value! #=> 20 or raise
result.error #=> nil or raises an exception
result.type #=> :integer
Step or Check
result = Zx.Success(5, type: :integer)
.step{ |number| number + 5 }
.on_success(:integer) {|number| puts number } #=> 10
.on(:success, :integer) {|number| puts number } #=> 10
.on_success {|number| puts number } #=> 10
result.success? #=> true
result.failure? #=> false
result.value #=> 10
result.value! #=> 10 or raise
result.error #=> nil or raises a n exception
result.type #=> :integer
result = Zx.Success(5, type: :integer)
.step{ |number| number + 5 }
.check { |number| number == 10 }
.on_success{|number| puts number } #=> 10
result.success? #=> true
result.failure? #=> false
result.value #=> 10
result.value! #=> 10 or raise
result.error #=> nil or raises an exception
result.type #=> :integer
result = Zx.Success(5, type: :integer)
.step{ |number| number + 5 }
.check { |number| number == 15 }
.on_failure{|error| puts error } #=> 10
You can use one or multiples listeners in your result. We see some use cases.
Simple composition
class AsIncluded
include Zx
def pass(...)
Success(...)
end
def passthrough(value)
Success[value]
end
def failed(error)
Failure[error, type: :error]
end
end
result = AsIncluded.new.pass('save record!')
result
.on(:success, :success) { expect(_1).to eq(a: 1) }
.on(:success, :mailer) { expect(_1).to eq(a: 1) }
.on(:success, :persisted) { expect(_1).to eq('save record!') }
.on(:success) { |value, (type)| expect([value, type]).to eq(['save record!', :persisted]) }
.on(:failure, :error) { expect(_1).to eq('on error') }
.on(:failure, :record_not_found) { expect(_1).to eq('not found user') }
Simple Inherit
class AsInherited < Zx::Result
def pass(...)
Success(...)
end
def passthrough(value)
Success[value]
end
def failed(error)
Failure(error, type: :error)
end
end
result = AsInherited.new.pass('save record!')
result
.on(:success, :success) { expect(_1).to eq(a: 1) }
.on(:success, :mailer) { expect(_1).to eq(a: 1) }
.on(:success, :persisted) { expect(_1).to eq('save record!') }
.on(:success) { |value, (type)| expect([value, type]).to eq(['save record!', :persisted]) }
.on(:failure, :error) { expect(_1).to eq('on error') }
.on(:failure, :record_not_found) { expect(_1).to eq('not found user') }
You can use directly methods, for example:
Zx::Result.Success(relation)
# or
Zx::Result::Success[relation]
# or
Zx::Success[relation]
Zx::Result.Failure('error', type: :invalid)
# or
Zx::Result::Failure[:invalid_user, 'user was not found']
# or
Zx::Failure[:invalid_user, 'user was not found']
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run bundle exec rspec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/thadeu/zx-result. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.