In this post I am not going to discuss how to use Plug but rather explore how it works ! This is because I find the code written using Plug to be really neat and if you have read the first link you would feel the same ! But if you didn't I will try to trigger your curiosity with the following example which, by the way, is taken from the Phoenix project:

defmodule HelloPhoenix.MessageController do

use HelloPhoenix.Web, :controller



def show(conn, params) do

case authenticate(conn) do

{:ok, user} ->

case find_message(params["id"]) do

nil ->

conn |> put_flash(:info, "That message wasn't found") |> redirect(to: "/")

message ->

case authorize_message(conn, params["id"])

:ok ->

render conn, :show, page: find_page(params["id"])

:error ->

conn |> put_flash(:info, "You can't access that page") |> redirect(to: "/")

end

end

:error ->

conn |> put_flash(:info, "You must be logged in") |> redirect(to: "/")

end

end

end

Don't be intimidated by the above code just focus on the show() function here. Observe the case statements, in outermost case statement we try to authenticate user, if this operation succeeds we have another nested case statement that tries to find message and if it succeeds we have yet another nested case statement which tires to authorize it. Notice how these set of dependant operations, by dependant I mean that the next operation is executed only if the previous is executed successfully, leads to a nested and unreadable code. Now the above code when written using Plug looks as follows:

defmodule HelloPhoenix.MessageController do

use HelloPhoenix.Web, :controller



plug :authenticate

plug :find_message

plug :authorize_message



def show(conn, params) do

render conn, :show, page: find_page(params["id"])

end



defp authenticate(conn, _) do

case Authenticator.find_user(conn) do

{:ok, user} ->

assign(conn, :user, user)

:error ->

conn |> put_flash(:info, "You must be logged in") |> redirect(to: "/") |> halt

end

end



defp find_message(conn, _) do

case find_message(params["id"]) do

nil ->

conn |> put_flash(:info, "That message wasn't found") |> redirect(to: "/") |> halt

message ->

assign(conn, :message, message)

end

end



defp authorize_message(conn, _) do

if Authorizer.can_access?(conn.assigns[:user], conn.assigns[:message]) do

conn

else

conn |> put_flash(:info, "You can't access that page") |> redirect(to: "/") |> halt

end

end

end

Notice that all the nested operations have been converted into individual functions and on top we have defined an invocation sequence i.e.

plug :authenticate

plug :find_message

plug :authorize_message

using the plug macro. Isn't this cool ! You can write whatever functions you want and define a pipeline sequence (as above) over them. This way the error handling for a particular operation is contained in the function itself rather than being all over the place.

I hope the above example is intriguing enough to investigate the sorcery behind Plug ! But before we have look at the source code let's see how to write a plug

defmodule MyPlug do

use Plug.Builder



plug :set_header

plug :send_ok



def set_header(conn, _opts) do

put_resp_header(conn, "x-header", "set")

end



def send_ok(conn, _opts) do

send(conn, 200, "ok")

end

end

The above code example has been taken from Elixir docs and we will call it “the basic plug”, keep this name in mind as I will be using it quite a few times. The code seems pretty much similar to previous plug code we have seen, apart from the use Plug.Builder statement. To understand this code we dive into the Plug source code which is openly available !

The link below is the only module you will need to understand from the Plug source code to get a basic picture of what is going on.

https://github.com/elixir-lang/plug/blob/master/lib/plug/builder.ex

Also there some links available at the end of this article which will prove to be useful while we dissect the above source code. So I suggest you open them in separate tabs, come back and read on … don't get let stuck on those links while opening new tabs … do come back !