Ytry
A Scala inspired gem that introduces Try
s to Ruby while aiming for an idiomatic API.
Installation
Add this line to your application's Gemfile:
gem 'ytry'
And then execute:
$ bundle
Or install it yourself as:
$ gem install ytry
Basic usage
The Try type represents a computation that may either result in an error, or return a successfully computed value.
If the block passed to Try runs with no errors, then a Success
wrapping the computed value is returned.
An instance of Failure
wrapping the error is returned otherwise.
require 'ytry'
include Ytry
Try { 1 + 1 } # Success(2)
Try { 1 / 0 } # Failure(#<ZeroDivisionError: divided by 0>)
Success
and Failure
provide a unified API that lets us express a sequence of tranformations in a fluent way, without error handling cluttering the flow:
def load_and_parse json_file
Try { File.read(json_file) }
.map {|content| JSON.parse(content)}
.select {|table| table.is_a? Array}
.recover {|e| puts "Recovering from #{e.message}"; []}
end
load_and_parse(nonexisting_file) # prints "Recovering from No such file..." # Success([])
load_and_parse(wrong_format_file) # prints "Recovering from Element not found" # Success([])
load_and_parse(actual_file) # Success([{"id"=>1, "name"=>"Lorenzo", "dob"=>"22/07/1985"}])
Try#map
and Try#recover
are means to interact with the value wrapped by a Try in a safe way - i.e. with no risk of errors being raised.
Try#select
transforms a Success into a Failure when the underlying value does not satisfy the given predicate - i.e. the given block returns false. That can be useful when validating some input.
Try#get_or_else
provides a safe way of retrieving the possibly-missing value it contains. It returns the result of the given block when the Try is a Failure. It is equivalent to Try#get
when the Try is a Success.
invalid_json = "[\"missing_quote]"
Try { JSON.parse(invalid_json) }
.get_or_else{ [] } # []
Try { JSON.parse("[]") }
.get_or_else { fail "this block is ignored"} # []
It is preferable to use Try#get_or_else
over Try#get
, as #get
will raise an error when called on a Failure. It is possible to check for failure via #empty?
, but that tipically leads to non-idiomatic code
Why Try?
Using Try instead of rescue blocks can make your software both clearer and safer as it
- leads to less verbose error handling
- simplifies the way we deal with operations that might fail for several reasons (such as IO operations)
- privileges method chaining thus reducing the need for auxiliary variables to store intermediate results in a computation
- encourages programming towards immutability, where the data is transformed rather than mutated in place.
License
The gem is available as open source under the terms of the MIT License.