Result
∠(・.-)―〉 →◎ Result adds Monadic Error Handling capabilities to Crystal lang, inspired by Result in Rust lang, Monad and the Elixir lang approach (state return).
Adapted to be productive in Crystal and Domain-Driven Design (DDD).
Installation
- Add the dependency to your 
shard.yml: 
   dependencies:
     result:
       github: nicolab/crystal-result
       version: ~> 2.0.1 # Check the latest version!
- Run 
shards install 
Usage
require "result"
require "result/utils"
# With a basic *value*
def something(value : Number) : Result
  # Wrap value into a `Result` instance (struct: `Ok` or `Err`).
  # If an error occurred, here *something* method returns an `Err` instance.
  res = result!(value)
  # Executed only if `result!` (above) has returned an `Ok` instance,
  pp "does something"
  pp res.unwrap # => Number
  res
end
# With a `Result` instance
def something(res : Result) : Result
  # Try to unwrap original value.
  # If result is an error, here *something* method returns this `Err` instance.
  data = try!(res)
  # Executed if `try!` (above) has returned the original value.
  pp "does something"
  # Original value
  pp data
  # Wrap data into a `Result` struct:
  # Returns success
  # `Ok` / `Ok.status # => :done`
  Ok.done data
  # Or returns an error
  # `Err` / `Err.status # => :fail`
  Err.fail "Oops!"
end
# Try to unwrap a *Result* (like `Result#unwrap`) or forward the value if it is not a `Result`.
res = Ok.done("hello") # or `Ok.new("hello")`
value = unwrap!(res) # => "hello"
res = Err.fail("Oops") # or `Err.new("Oops")`
value = unwrap!(res) # => raise Exception.new "Oops"
foo = "bar"
value2 = unwrap!(foo) # => "bar"
To go further, Result works wonderfully with fuzzineer/match-crystal.
require "result"
require "result/utils"
require "match-crystal"
res = something()
message = match res.status, {
  :created   => "Created with success",
  :destroyed => "Destroyed with success",
  :pending   => "Pending task",
  :input     => "Bad argument",
  :fail      => "Failed",
  _          => "anything else!",
}
puts message
# other example
message = match res, {
  Ok(String)              => "Ok is a good string",
  res.status? :created    => "Created with success",
  Ok                      => "It's ok",
  Err(ArgumentError)      => "Bad argument",
  res.status? :not_found  => "Not found",
  Err                     => ->{
    puts "Block is supported using Proc syntax"
    "Error occurred"
  },
}
puts message
Example with a case:
message = case res
  when .status? :created
    "Created with success"
  when .status? :destroyed
    "Destroyed with success"
  when .status? :pending
    "Pending task"
  when .status? :input
    "Bad argument"
  when .status? :not_found
    "Not found"
  when .status? :fail
    "Failed"
  when Ok
    "Another success"
  when Err
    "Another error"
  else
    "Anything else!"
  end
puts message
Works well with a controller.
Development
crystal spec
crystal tool format
Contributing
- Fork it (https://github.com/Nicolab/crystal-result/fork)
 - Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
 
LICENSE
MIT (c) 2020, Nicolas Talle.
Author
|  |
|---|
| Nicolas Talle |
| 
 |