Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/vic/happy
the alchemist's happy path with elixir
https://github.com/vic/happy
elixir error-handling monad with
Last synced: about 2 months ago
JSON representation
the alchemist's happy path with elixir
- Host: GitHub
- URL: https://github.com/vic/happy
- Owner: vic
- License: apache-2.0
- Created: 2016-01-18T09:20:37.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2017-02-20T22:54:02.000Z (almost 8 years ago)
- Last Synced: 2024-10-11T22:23:52.581Z (2 months ago)
- Topics: elixir, error-handling, monad, with
- Language: Elixir
- Homepage: http://hex.pm/packages/happy
- Size: 427 KB
- Stars: 45
- Watchers: 4
- Forks: 3
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- freaking_awesome_elixir - Elixir - Happy path programming, alternative to elixir `with` form. (Errors and Exception Handling)
- fucking-awesome-elixir - happy - Happy path programming, alternative to elixir `with` form. (Errors and Exception Handling)
- awesome-elixir - happy - Happy path programming, alternative to elixir `with` form. (Errors and Exception Handling)
README
# Happy
[![help maintain this lib](https://img.shields.io/badge/looking%20for%20maintainer-DM%20%40vborja-663399.svg)](https://twitter.com/vborja)the alchemist's [happy path](https://en.wikipedia.org/wiki/Happy_path) with elixir
*If you are using Elixir >= 1.2, you might prefer [happy_with](http://github.com/vic/happy_with)*
- [Installation](#installation)
- [About](#about)
- [Usage](#usage)## Installation
[Available in Hex](https://hex.pm/packages/happy), the package can be installed as:
1. Add happy to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:happy, "~> 1.3.1"}]
end
```## About
Ok, so I was just trying to find a nice way (beautiful syntax, yet flexible enough) to handle
errors in elixir. Handling `:ok`/`:error` like tuples without lots of `if`/`case`s.After creating [ok_jose](https://github.com/vic/ok_jose), looking at Elixir's [with](http://elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html#with/1) special form and other
[alternatives](https://github.com/ruby2elixir/plumber_girl), I wanted to create this tiny library with the
following goals in mind:- The [happy path](https://en.wikipedia.org/wiki/Happy_path) must be immediately obvious to the eyes.
- Code should not be cluttered and should just work using the elixir you already know.
- Avoid introducing noisy operators `~>>`, requiring commas after each pattern
- Should provide a way to recover when not so happy moments come.If you dont need special features like tags, and are using elixir ~> 1.2, checkout [`happy_with`](http://github.com/vic/happy_with) wich is just tiny sugar around `with` special form.
## Usage
```elixir
import Happy
```##### the `happy_path` macro
The `happy_path` macro takes a `do` block and rewrites any first-level pattern matching expression into a `case`.
```elixir
happy_path do
{:ok, b} = a
{:ok, d} = b
c(d)
end
```gets rewritten to something like:
```elixir
case(a) do
{:ok, b} ->
case (b) do
{:ok, d} -> c(d)
end
end
```Note that a variable pattern match (assignment) is not
rewritten, as it will always match and would cause warnings.```elixir
happy_path do
x = some(thing) # simple assignment is left as is
end
```###### handling errors with `else` clauses
If you want to handle non-matching values,
provide an `else` block with additional
matching clauses:```elixir
happy_path do
{:ok, b} = a
c(b)
else
{:error, x} -> x
end
```###### sharing common error handling code
Sometimes you would want to share common error handling
code on many happy_paths, for example in an api controller
with many actions, all of which handle common invalid cases
like parameter validation.In those cases you can provide `happy_path` with an
default error handler as first argument. Note that if no *local
else clause* matches, the error value is *piped* into
the provided error handler. Thus the handler is anything
you can pipe the error value into.```elixir
happy_path(else: handler) do
{:ok, x} = foo
x + 1
else
{:error, y} -> y
end
```gets rewritten to something like:
```elixir
case foo do
{:ok, x} ->
x + 1
{:error, y} ->
y
err ->
err |> handler
end
```###### support for guards
Just like with `case` you can include guard tests.
```elixir
happy_path do
x when not is_nil(x) = some(foo)
x + 1
end
```###### tags
Tags is an special feature of `happy_path` not found on
alternatives like elixir's `with` expression.Tags look like module attributes but they are not, they
are just shorthand for tagging a pattern.```elixir
happy_path do
# using the `foo` tag
@foo {:ok, x} = y# is exactly the same as
{:foo, {:ok, x}} = {:foo, y}
else
{:foo, {:error, e}} -> "Foo error"
end
```Tags can help error handlers to get a clue about which
context the mismatch was produced on. It's mostly useful
for distingishing between lots of `{:error, _}` like tuples.###### @happy tag
The special tag `@happy` lets you mark a pattern matching expression
to be skipped by `happy_path`. For example when you know something
will always match.```elixir
happy_path do
@happy {this, would} = {"always", "match"}
end
```produces just:
```elixir
{this, would} = {"always", "match"}
```##### Example usage in a web application creating a user
```elixir
happy_path do%{valid?: true} = ch = User.changeset(params)
{:ok, user} = Repo.insert(ch)
render(conn, "user.json", user: user)else
%{valid?: false} = ch -> render(conn, "validation_errors.json", ch: ch)
{:error, ch} -> render(conn, "db_error.json", ch: ch)
_ -> text(conn, "error")end
```## Is it any good?
[Yes](https://news.ycombinator.com/item?id=3067434)