{"id":13507856,"url":"https://github.com/vic/happy","last_synced_at":"2025-03-16T14:30:56.571Z","repository":{"id":57504367,"uuid":"49865674","full_name":"vic/happy","owner":"vic","description":"the alchemist's happy path with elixir","archived":false,"fork":false,"pushed_at":"2017-02-20T22:54:02.000Z","size":437,"stargazers_count":45,"open_issues_count":2,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-27T10:35:44.289Z","etag":null,"topics":["elixir","error-handling","monad","with"],"latest_commit_sha":null,"homepage":"http://hex.pm/packages/happy","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-18T09:20:37.000Z","updated_at":"2024-10-07T22:23:46.000Z","dependencies_parsed_at":"2022-08-28T02:01:51.767Z","dependency_job_id":null,"html_url":"https://github.com/vic/happy","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fhappy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fhappy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fhappy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fhappy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vic","download_url":"https://codeload.github.com/vic/happy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243818195,"owners_count":20352629,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","error-handling","monad","with"],"created_at":"2024-08-01T02:00:41.124Z","updated_at":"2025-03-16T14:30:55.949Z","avatar_url":"https://github.com/vic.png","language":"Elixir","funding_links":[],"categories":["Errors and Exception Handling","Macros"],"sub_categories":[],"readme":"# Happy \u003ca href=\"https://travis-ci.org/vic/happy\"\u003e\u003cimg src=\"https://travis-ci.org/vic/happy.svg\"\u003e\u003c/a\u003e\n[![help maintain this lib](https://img.shields.io/badge/looking%20for%20maintainer-DM%20%40vborja-663399.svg)](https://twitter.com/vborja)\n\n\nthe alchemist's [happy path](https://en.wikipedia.org/wiki/Happy_path) with elixir\n\n*If you are using Elixir \u003e= 1.2, you might prefer [happy_with](http://github.com/vic/happy_with)*\n\n- [Installation](#installation)\n- [About](#about)\n- [Usage](#usage)\n\n## Installation\n\n[Available in Hex](https://hex.pm/packages/happy), the package can be installed as:\n\n  1. Add happy to your list of dependencies in `mix.exs`:\n\n```elixir\n  def deps do\n    [{:happy, \"~\u003e 1.3.1\"}]\n  end\n```\n\n## About\n\nOk, so I was just trying to find a nice way (beautiful syntax, yet flexible enough) to handle\nerrors in elixir. Handling `:ok`/`:error` like tuples without lots of `if`/`case`s.\n\nAfter 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\n[alternatives](https://github.com/ruby2elixir/plumber_girl), I wanted to create this tiny library with the\nfollowing goals in mind:\n\n- The [happy path](https://en.wikipedia.org/wiki/Happy_path) must be immediately obvious to the eyes.\n- Code should not be cluttered and should just work using the elixir you already know.\n- Avoid introducing noisy operators `~\u003e\u003e`, requiring commas after each pattern\n- Should provide a way to recover when not so happy moments come.\n\nIf you dont need special features like tags, and are using elixir ~\u003e 1.2, checkout [`happy_with`](http://github.com/vic/happy_with) wich is just tiny sugar around `with` special form.\n\n## Usage\n\n```elixir\nimport Happy\n```\n\n##### the `happy_path` macro\n\nThe `happy_path` macro takes a `do` block and rewrites any first-level pattern matching expression into a `case`.\n\n```elixir\nhappy_path do\n  {:ok, b} = a\n  {:ok, d} = b\n  c(d)\nend\n```\n\ngets rewritten to something like:\n\n```elixir\ncase(a) do\n  {:ok, b} -\u003e\n    case (b) do\n      {:ok, d} -\u003e c(d)\n    end\nend\n```\n\nNote that a variable pattern match (assignment) is not\nrewritten, as it will always match and would cause warnings.\n\n```elixir\nhappy_path do\n  x = some(thing) # simple assignment is left as is\nend\n```\n\n###### handling errors with `else` clauses\n\nIf you want to handle non-matching values,\nprovide an `else` block with additional\nmatching clauses:\n\n```elixir\nhappy_path do\n  {:ok, b} = a\n  c(b)\nelse\n  {:error, x} -\u003e x\nend\n```\n\n###### sharing common error handling code\n\n\nSometimes you would want to share common error handling\ncode on many happy_paths, for example in an api controller \nwith many actions, all of which handle common invalid cases\nlike parameter validation.\n\nIn those cases you can provide `happy_path` with an\ndefault error handler as first argument. Note that if no *local\nelse clause* matches, the error value is *piped* into \nthe provided error handler. Thus the handler is anything\nyou can pipe the error value into.\n\n```elixir\nhappy_path(else: handler) do \n {:ok, x} = foo\n x + 1\nelse\n {:error, y} -\u003e y\nend\n```\n\ngets rewritten to something like:\n\n```elixir\ncase foo do\n  {:ok, x} -\u003e \n    x + 1\n  {:error, y} -\u003e\n    y\n  err -\u003e \n    err |\u003e handler\nend\n```\n\n###### support for guards\n\nJust like with `case` you can include guard tests.\n\n```elixir\nhappy_path do\n  x when not is_nil(x) = some(foo)\n  x + 1\nend\n```\n\n###### tags\n\nTags is an special feature of `happy_path` not found on\nalternatives like elixir's `with` expression.\n\nTags look like module attributes but they are not, they\nare just shorthand for tagging a pattern.\n\n```elixir\nhappy_path do\n  # using the `foo` tag\n  @foo {:ok, x} = y\n\n  # is exactly the same as\n  {:foo, {:ok, x}} = {:foo, y}\nelse\n  {:foo, {:error, e}} -\u003e \"Foo error\"\nend\n```\n\nTags can help error handlers to get a clue about which\ncontext the mismatch was produced on. It's mostly useful\nfor distingishing between lots of `{:error, _}` like tuples.\n\n###### @happy tag\n\nThe special tag `@happy` lets you mark a pattern matching expression\nto be skipped by `happy_path`. For example when you know something\nwill always match.\n\n```elixir\nhappy_path do\n  @happy {this, would} = {\"always\", \"match\"}\nend\n```\n\nproduces just:\n\n```elixir\n{this, would} = {\"always\", \"match\"}\n```\n\n\n##### Example usage in a web application creating a user\n\n```elixir\nhappy_path do\n\n  %{valid?: true} = ch = User.changeset(params)\n  {:ok, user} = Repo.insert(ch)\n  render(conn, \"user.json\", user: user)\n\nelse\n\n  %{valid?: false} = ch -\u003e render(conn, \"validation_errors.json\", ch: ch)\n  {:error, ch} -\u003e render(conn, \"db_error.json\", ch: ch)\n  _ -\u003e text(conn, \"error\")\n\nend\n```\n\n\n\n## Is it any good?\n\n[Yes](https://news.ycombinator.com/item?id=3067434)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvic%2Fhappy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvic%2Fhappy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvic%2Fhappy/lists"}