Ytry
A Scala inspired gem that introduces Trys 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.