ruevaughn
Elixir Plugs - question about Plug Modules
Hi everyone, I am very new to the Erlang / Elixir / Phoenix scene, so sorry if i’m posting this in the wrong spot, or my terminology is off, feel free to correct me on anything since i’d rather learn.
I’m currently reading and following along with the “Programming Phoenix” book and have a few question about module plugs.
So far, what I understand is that for a module plug to be a ‘module plug’, it needs two functions, init/1 and call/2.
In the book, we make a module called Rumbl.Auth. This module uses the expected init/1 and call/2 functions, but also adds others, such as login/2, login_by_username_and_pass/4 and some others.
Are these ‘other’ functions (login, and login_by_username_and_pass) relying on call/2 in order to operate properly? Or are they completely isolated and ran independently?
Here is the Plug i’m working on… auth module plug
My question is due to me trying to understand when ‘call’ is actually being executed by Phoenix.
I understand I add the module to my router like so
defmodule Rumbl.Router do
use Rumbl.Web, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug Rumbl.Auth, repo: Rumbl.Repo
end
But i’m not sure when ‘call’ is actually getting called since I never explicitly call it. Is it getting called every time I run another function within that module? Or does it get called once? I understand init gets called during compile time to set things up for call, and call/2 gets executed at runtime.
I guess i’m confused why ‘call’ is even needed in this case, and why I can’t just treat the module (other than the fact that it needs call/2 to be considered a plug) as a module. Is call there just for the sake of being called a ‘plug’?
I hope my question makes sense. Sorry if it’s something blatant i’m missing. Excited to read any replies. Been really enjoying Functional / Erlang / Elixir / Phoenix so far… I haven’t been this excited to learn something in a long time.
Most Liked
hubertlepicki
Phoenix actually never performs the “call” itself as far as I can tell. Phoenix is nothing more than a set of plugs and supporting libraries.
You are right about the init function, it gets called only when app stars. The call function, on the other hand, will be called on each request.
When a HTTP request comes in, Cowboy server is the first that starts handling such request. plug library adds hooks, builds up the chain of plug middlewares, and calls it, I believe here: plug/lib/plug/adapters/cowboy/handler.ex at 2df667389dbaebb0e2102343c8d7a6ed4de6e280 · elixir-plug/plug · GitHub
So, calling “call” happens on each and every request. Moreover, those calls get nested so a pipeline of plugs get processed by some macro (plug/lib/plug/builder.ex at main · elixir-plug/plug · GitHub) and when one call finishes, another call is executed, until some plug returns a valid response, in such case it is halted, response returned to the browser by cowboy. You don’t have to manually call upstream plugs because of smart macros that do it for us.
Basically, a request comes in → cowboy handler executed → plug1.call() → plug2.call() → plug3.call() etc.
OvermindDL1
Actually closer to this (still not perfectly accurate, but keeps the init in this idea):
conn # the initial conn handled by cowboy, passed on to Phoenix
|> accepts(["html"]))
|> fetch_session()
|> fetch_flash()
|> protect_from_forgery()
|> put_secure_browser_headers()
|> Rumbl.Auth.call(Rumbl.Auth.init(repo: Rumbl.Repo))
But yeah, it is the ‘pipeline’ call that does the plugs there. In something like Endpoint the plugs are run via the plug builder that builds the ‘state’. ![]()
bobbypriambodo
You can think of this:
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug Rumbl.Auth, repo: Rumbl.Repo
end
will generate this:
conn # the initial conn handled by cowboy, passed on to Phoenix
|> accepts(["html"])
|> fetch_session
|> fetch_flash
|> protect_from_forgery
|> put_secure_browser_headers
|> Rumbl.Auth.call(repo: Rumbl.Repo)
Any request that goes to your app will be run through that pipeline first. You don’t have to explicitly call call/2.
Edit: actually my explanation is missing some details. Before going through the pipeline, the connection from first arrives at the Endpoint and went through a series of Plugs before going to the Router and, subsequently, the pipeline.
Does that make sense?
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









