Subscribe to access all episodes. View plans →
Published September 25, 2017
Elixir 1.4.2
Phoenix 1.3.0
Plug 1.4.3
View source on GitHub
If you’re building a web application in Elixir you’re using Plug.
According to the docs, Plug is a specification for composable modules between web applications.
At the heart of Plug is the connection, which is represented by the Plug.Conn
struct, this contains all the data for a given request.
A plug simply takes a connection struct, and returns a connection struct.
This allow us to take our connection, check and modify it if needed, and pass it down for other plugs to use.
Plugs come in two flavors - functions and modules.
The best way to understand how Plugs work is to build one - so let’s get started.
In our movie app, let’s display the number of movies on every page.
Now in our movie controller we could add logic to get our movie count and add it to every action, but that would create a lot of duplicate code.
Let’s create a function plug that gets our movie total and assigns it to our connection.
First we’ll create a new module called MovieData
.
Inside it we’ll import Plug.Connection
.
Then we’ll create our function plug named ‘movie_total’.
A function plug takes two arguments, the connection and a set of options. Since we don’t won’t use the options in our function, we can ignore them.
Inside our function we’ll use Plug’s assign function to assign our movie count to the connection.
Let’s start with a default value of 3 to test it out.
lib/teacher/movie_data.ex
defmodule Teacher.MovieData do
import Plug.Conn
def movie_total(conn, _opts) do
assign(conn, :movie_total, 3)
end
end
Now let’s go back to our MovieController
and include our plug.
We’ll import the MovieData
module.
Then we’ll call plug :movie_total
.
lib/teacher_web/controllers/movie_controller.ex
defmodule TeacherWeb.MovieController do
…
import Teacher.MovieData
plug :movie_total
…
end
Now let’s open our “app.html.eex”
And we’ll add a movie total, which we should be able to fetch from the connection’s assigns as “movie_total”.
Template path: lib/teacher_web/templates/layout/app.html.eex
<html>
...
<p>Movie total: <%= @conn.assigns[:movie_total] %></p>
...
</html>
With that let’s start up our server.
$ mix phx.server
And if we open our movie app, we should see the movie total of three displayed.
Great now let’s update our function plug to get the real count from the database.
Back in MovieData
we’ll need Ecto.Query
’s “from” function so let’s import it.
Then we’ll also need to alias our Repo
and Movie
modules.
Great with that let’s use Repo.one and give it a query that gets the count of all movies in our database.
Then we can update the default value with the actual count.
lib/teacher/movie_data.ex
defmodule Teacher.MovieData do
import Plug.Conn
import Ecto.Query, only: [from: 2]
alias Teacher.Repo
alias TeacherWeb.Movie
def movie_total(conn, _opts) do
movie_total = Repo.one(from m in Movie, select: count("*"))
assign(conn, :movie_total, movie_total)
end
end
And going back to our browser, we see our movie count is displaying the correct number of movies. And it’s being displayed on each page.
Yuriy Marchenko
7 years agoThanks for doing this!
Alekx
7 years agoNo problem - thanks for watching!
Nicolas Blanco
7 years agoGreat cast!
I was wondering the good syntax to use in templates to access a variable in
conn.assigns
betweenconn.assigns[:foo]
and just@foo
.Basically if you use
conn.assigns[:foo]
in the template andfoo
does not exist, it will output nothing in the template. If using just@foo
, it will raise an exception if the variable does not exist.So depending if you expect the variable to be there or maybe not you would use the first or second method, right?
Alekx
7 years agoThanks Nicolas!
As a general rule I think you’re correct - if you expect it to be there use
@foo
and if it may or may not be there, then access it throughconn.assigns[:foo]
.Pedro Luz
7 years agoIs any way people can support you (elixircasts.io), in any way that would allow you to continue doing this?
I think that there is immense value in a website like this (specially for someone like me that just started to do small things in elixir/phoenix), but most of them, due to the hard work that it takes to make it happen, fail. I know that takes time and dedication, and we can easily see that there is dedication in it.
Thanks for the awesome work. Keep them coming.
Alekx
7 years agoPedro, thanks for you kind words. I’m currently weighing different options for something like this.
And don’t worry, I’ll keep the episodes coming :)