For anyone that likes with
I encourage you to look at the Happy package, it is with
but done better, a more simple syntax, better error handling, and more. I use it extensively in ways where with would be substantially more verbose, and yet it is trivially simple in implementation. It is what I think with
should have been. With is more like a single-loop comprehension (that is what <-
tells me), where happy_path
is more like a block that you expect to succeed, yet you handle its errors with utter ease.
Here is a simple example of it from one of my projects:
def index(conn, _params) do
happy_path!(else: handle_error(conn)) do
@perm true = conn |> can?(index(%Perms.Help{}))
render(conn, :index)
end
end
This checks if the user has permissions, if not it will redirect them via my default handle_error
callback to the login page (you can also handle errors in-line too).
A more complex example:
def show(conn, %{"sid" => sid_param, "id" => sem_id_param}) do
happy_path!(else: handle_error(conn)) do
@perm true = conn |> can?(show(%Perms.Nursing.Student.Semester{}))
{:ok, {_cnum, uid, _obj, student}} = verify_nursing_student(sid_param)
@perm true = conn |> can?(show(%Perms.Nursing.Student.Semester{uid: uid}))
{:ok, {sem_id, semester}} = verify_nursing_semester(sem_id_param)
semesters = Repo.all(from sem in Student.Semester, where: sem.uid == ^uid and sem.semester_id == ^sem_id)
render(conn, :show, semesters: semesters, sid: sid_param, semester: semester, student: student)
end
end
This one checks if they have permission to the area, then tries to convert the sid string to an integer and fetch the student (returning an :error tuple if it fails), then checks they have permission for this specific student, then gets all semesters for the student, and finally renders it. If any of it fails my handle_error
callback handles it (I have some functions that have special in-line error handling too, but those are rare, I try to follow my handle_error for proper webserver results). It will do unauthorized on permissions issues (and redirect to login saving the current path), it will do bad request if the conversion from string->data could not be done, etc… etc… It has significantly simplified my phoenix code.
Read the docs on it to see how powerful and succinct it is (and it has examples in both the docs and tests on the code it converts it to, it is extremely straightforward).
The same author also made ok_jose as a simplified pipe operator that works over, by default, {:ok/:error, data} tuples, though you can define your own shapes too.
EDIT: Also, happy_path
is older than with, I’ve not clue why with was not modeled on it… It was never mentioned at all in talks, so they might not have known. But really, with
has nothing over happy_path
.