Releases: expede/exceptional
More raising helpers
Added ensure!/1
and def!/2
ensure
is essentially an alias for foo >>> fn x -> x end.()
ensure!([1, 2, 3])
#=> [1, 2, 3]
%ArgumentError{message: "raise me"} |> ensure!
#=> ** (ArgumentError) raise me
def! foo(...)
helps define a def foo(...)
plus def foo!(...)
variant that raises any final exception from foo
defmodule Foo do
use Exceptional
def! foo(a), do: a
end
Foo.foo([1, 2, 3])
#=> [1, 2, 3]
Foo.foo(%ArgumentError{message: "raise me"})
#=> %ArgumentError{message: "raise me"}
Foo.foo!([1, 2, 3])
#=> [1, 2, 3]
Foo.foo!(%ArgumentError{message: "raise me"})
#=> ** (ArgumentError) raise me
Extract Phoenix helpers
Phoenix/Exceptional helpers now live here
(Very) simple Phoenix error view helpers
Phoenix Error View Helpers
Automated rendering of error views based on the message
field on exception structs.
This is often sufficient for simple JSON cases and pre-release HTML. It is recommended to write custom error views before production to give your users as much detail as possible in the user friendliest way that their content type allows.
# /web/views/error_view.ex
defrender :error, for: 401, do: "Not authorized"
defrender :error, for: 404, only: [:json], do: "Data not found"
defrender :error, for: 422, except: [:html] do: "Unprocessible entity"
defrender :error, for: 500, do: "Server internal error"
# Example JSON Error Response
404 %{error: "Data not found", reason: "Photo deleted"}
# Example HTML Error Response
500 "Server internal error"
1.4.0
A long-requested feature. I'm finally caving and writing it 😜
normalize(42)
#=> 42
normalize(%Enum.OutOfBoundsError{message: "out of bounds error"})
#=> %Enum.OutOfBoundsError{message: "out of bounds error"}
normalize(:error)
#=> %ErlangError{original: nil}
normalize({:error, "boom"})
#=> %ErlangError{original: "boom"}
normalize({:error, {1, 2, 3}})
#=> %ErlangError{original: {1, 2, 3}}
normalize({:error, "boom with stacktrace", ["trace"]})
#=> %ErlangError{original: "boom with stacktrace"}
normalize({:good, "tuple", ["value"]})
#=> {:good, "tuple", ["value"]}
Something similar was suggested back in #2. I still believe that you should pattern match as much as possible, making normalize
superfluous... but this pipes well enough in most situations.
This isn't a complete solution for all tagged tuples. In fact, it is emphatically not that. For example:
normalize({:my_special, "tuple", ["value"]})
#=> {:my_special, "tuple", ["value"]}
☝️ You'll still need to handle those cases in pattern matches yourself (unless you do want the tuple value, which is a common case). To do this, you may pass an additional converting function. Example:
{:oh_no, {"something bad happened", %{bad: :thing}}}
|> normalize(fn
{:oh_no, {message, _}} -> %File.Error{reason: message}) # This case
{:bang, message -> %File.CopyError{reason: message})
otherwise -> otherwise
end)
#=> %File.Error{message: msg}
{:oh_yes, {1, 2, 3}}
|> normalize(fn
{:oh_no, {message, _}} -> %File.Error{reason: message})
{:bang, message -> %File.CopyError{reason: message})
otherwise -> otherwise # This case
end)
#=> {:oh_yes, {1, 2, 3}}
The advantage for all of the common patterns is that we have a single struct representation for pattern matching (unless the error tuple is not one of the most common cases). Hopefully including this function doesn't cause any confusion 😨 Time will tell...
Make dangerous functions safe
safe/1
& safe/2
(plus pun aliases: lower/1
and lower/2
make functions that raise exceptions return those exceptions instead.
Does not catch throw
s, because that causes all sorts of odd behaviour (REPL errors returned as tuples or (structs if normalized), then they get piped into functions... all sorts of mess).
toothless_fetch = safe(&Enum.fetch!/2)
[1,2,3] |> toothless_fetch.(1)
#=> 2
toothless = safe(&Enum.fetch!/2)
[1,2,3] |> toothless.(999)
#=> %Enum.OutOfBoundsError{message: "out of bounds error"}
safe(&Enum.fetch!/2).([1,2,3], 999)
#=> %Enum.OutOfBoundsError{message: "out of bounds error"}
~~~ operator & more uses
~~~
Added a unary ~~~
operator for converting back to a tagged status.
More use
s
# Default
use Exceptional
# Only operators
use Exceptional, only: :operators
#Only named functions
use Exceptional, only: :named_functions
# If you like to live extremely dangerously. This is _not recommended_.
# Please be certain that you want to override the standard lib before using.
use Exceptional, include: :overload_pipe
if_exception
Added if_exception
, which has the ability to take the familiar forms:
if_exception [1,2,3], do: fn %{message: msg} -> msg end.(), else: Quark.id
if_exception [1,2,3] do
fn %{message: msg} -> msg end.()
else
Quark.id
end
v1.0.4
v1.0.0
First full version 🎉🎉🎉
Package allows you to delay raise
ing exceptions as long as possible, delegating control to the caller. Also provides a nice alternative to the {:tag, "message"}
pattern, with much more possible information, given that Exception
can be implemented for any struct.