Plug - Library for creating Web applications in Elixir

Jose Valim, the creator of Elixir, was part of the Rails Core team and author of the book “Crafting Rails Applications”. He received Ruby Hero award in 2010. He knew the concurrency limitations of Ruby. He was the Erlang User of the year in 2015. These facts show that he was well-versed in both Erlang and Ruby.  He wanted to use the concurrency model and performance of Erlang with the syntax of Ruby, as his intention was to create a Rails-like framework on concurrent and fault-tolerant Erlang VM. Elixir was born out of the marriage of productive Ruby with perfomant Erlang. Its syntax is that of Ruby but it is semantically closer to Erlang. That is it is compatible with Erlang and it compiles into Erlang byte-code, runs on Erlang VM and makes use of Erlang libraries. 

Elixir seems to have been created with an eye on web applications.
He had to create Rails-like features using a functional language. So he looked at a whole lot of technologies and borrowed some ideas from Clojure and C#/ LINQ which in turn was influenced by Monads in Haskell and its main creator Eric Meijer was a Haskell programmer. The Plug library is similar to Ruby’s Rake and Clojure’s Ring to take care of core details of HTTP and provide higher level components for others to build upon . The functions in Enum module operating on any in memory collection that implements the Enumerable Protocol resembles LINQ to Object and for database operations. Ecto library can use same syntax but had to implement Queryable in place of Enumerable protocol as in LINQ  to SQL. Valim calls this data polymorphism not found in Erlang.  Erlang was created to write programs for hardware/phone switches; so they avoided borrowing constructs from other languages unless they are absolutely essential for solving the problem at hand. They avoided lazy evaluation for the same reason. As the focus of Elixir is web applications and fetching data from disk-based database, they borrowed, apart from data polymorphism, the concept of Lazy evaluation also from LINQ/Haskell. The tool Mix is a combination of NPM and Grunt from JavaScript world and is similar to Leiningen from Clojure. The first version of Mix was created by the same people who created Leiningen. Its Lisp/Clojure -macros enable of extension of the language  / creation of DSL for specific domains. This meta-programming feature of Elixir enables people to contribute to Elixir and create libraries and frameworks.

Mix tool and Plug provide an easy way of using cowboy, rebar and other Erlang libraries.Plug was extracted from Dynamo, the Rail-like MVC framework, Jose Valim created. It is maintained by Core Elixir team to simplify web development.  Cowboy is a small, fast and modular HTTP server written in Erlang. Cowboy provides a complete HTTP stack, Websocket and REST. It has a small code base, is very efficient (both in latency and memory use) and can be easily embedded in another application. But it expects you to match your routes and their handlers in a certain format. Plug provides an adapter for cowboy and you can use the macros provided and furnish the information in a simpler DSL. Frameworks like Sugar and Phoenix built upon Plug take advantage of this feature.

Various Plugs

Plug carries out various functionalities 1) It provides an adapter for cowboy web server. 2) It also specifies how various components should be plugged. 3) It provides a library of components to be plugged. 4) It provides an API that abstracts away web framework functionalities and we can code basic web applications using the API. 5) Frameworks like Sugar and Phoenix are built on top of the above API to provide more advanced functionalities.

In Plug the request is represented as a %Plug.Conn{}, and this struct is passed from filter- functions, being slightly modified in each step, until we have a response that can be sent back.  It’s just that simple functional programming idea of passing data through functions until we get the result we want, and in this case the data happens to be an http request .It also ships with the convenience modules that make it easier to do a lot of things that are common to most applications, like creating pipelines, simple routers, dealing with cookies, headers, etc.  There are two kinds of plugs: module plugs and function plugs. A function plug is a single function. A module plug is a module that provides two functions init/1 and call/2 with some configuration details. Either way, they work the same. But module plugs can be re-used ie just stacked in a pipeline. There are different plugs to carry out different modifications to “Conn”, like parser, session, logger, router etc. But the last plug in the pipeline should be router which matches the incoming Conn and dispatches the “Conn” that contains the response or into which the response is piped as in UNIX. In other words the router plug tells the framework to terminate the pipeline by calling the send_resp() function. The function's call parameters are used to construct an HTTP response, which is returned to the web server which serves it to the client as shown below in Figure-1.

 

Figure-1

Sample

As mentioned above,Mix is a tool for Elixir similar to Leiningen in Clojure. It can be used to create and run and test the projects as well as for downloading and managing the dependencies.

We will use Mix to create an Elixir App. In the command prompt execute
 mix new basic_site  - -super // we ask Mix to generate a new app by name basic_site  and the flag -- super tells mix to add some boilerplate supervisor code.

 

An OTP application will be created by Mix. Open /lib/basic_site.ex, which already contains the code for an OTP application including code for start/0 function with the boilerplate code for a Supervisor. Before having a detailed look at it, we will have a look at  “mix.ex” – the configuration file of a mix project which contains all the information about the project, like name, version, elixir version used and dependencies- the applications it depends on, including the third party ones. To the dependencies we will add “”Plug and cowboy”.   We can make following modifications to the private function “defp deps do” which the “def project do” calls. Remember you cannot call private functions which can only be called by other functions within the module

{:plug, "~>1.0"},
{:cowboy, "~>1.0"}

The configuration information of the OTP application is contained in
“def application do”
Add :plug, :cowboy
to [applications:] , which earlier contains only :logger, It tells Mix that our OTP application will use cowboy and plug libraries.

The generated file with the required modifications is given below:

mix.ex

defmodule BasicSite.Mixfile do
use Mix.Project
#Project description
def project do
[app: :basic_site,
version: "0.0.1",
elixir: "~> 1.2",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
end
# Configuration for the OTP application
#
# Type "mix help compile.app" for more information
def application do
[applications: [:logger, :plug, :cowboy, :poison],  # run-time dependencies
mod: {BasicSite, []}] # module to start the application
end
# Dependencies can be Hex packages:
#
#   {:mydep, "~> 0.3.0"}
#
# Or git/path repositories:
#
#   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
#
# Type "mix help deps" for more examples and options
defp deps do
[{:plug, "~>1.0"},  #compile time dependencies which Mix should fetch
{:cowboy, "~>1.0"},
{:poison, “~1.4.0”}] #  will be used to send JSON response later.
end
end

Poison is a dependency for sending JSON responses, which we will use in the later part of the article .Now you can change to the project directory and execute                                                                                                                                                                              
mix deps.get
the necessary dependencies will be downloaded.

Application module

Application here means OTP application which spawns the worker processes and supervisors and manage them. The application Module is contained in basix_site.ex  mentioned above and its configuration information is contained in the application macro of the  Mix Module we saw above. As we know from our earlier Erlang OTP application, the application-specific code is contained in the callback module, while the behaviour in the library provides generic functionalities. Since the –sup option was provided to “mix new”, the Mix tool generates callback module containing code 1) for starting a supervisor process,
2) the children specification – the processes  that should be started when the supervisor is started and
3) supervisor specification, which tells how the supervisor should handle it when a child process abruptly dies.

The generated code is given below:

lib/basic_site.ex

defmodule BasicSite do
use Application
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do  # code starting the  supervisor process
import Supervisor.Spec, warn: false
# generated children specification is empty; it contains only comments; we have to fill it up
children = [
# Define workers and child supervisors to be supervised
# worker(BasicSite.Worker, [arg1, arg2, arg3]),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BasicSite.Supervisor] #supervisor specification is complete
Supervisor.start_link(children, opts)
end
end

The supervisor will be started when the application is run but no child process as the children specification is empty.
So we will add in the children the following:

worker(__MODULE__, [], function: :run),

From our earlier experience with OTP, we know MODULE is a macro which will be replaced with the actual module name at compile time. Empty list [] shows we can pass other arguments later and “run” is the function from which the worker process should be spawned. The function run may be defined as under:

def run do
{:ok, _} =Plug.Adapters.Cowboy.http(BasicSite.Router, [])
end

We want the supervisor to start a Cowboy process. To start cowboy process we will pass a Plug Router- which we will see in a separate file router.ex-  as argument to the function Plug.Adapters.Cowby.http(), as shown above. The completed basic_site.ex is given below:

lib/basic_site.ex

defmodule BasicSite do
use Application # Application behaviour
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do # start the supervisor process
import Supervisor.Spec, warn: false
children = [
# Define workers and child supervisors to be supervised
worker(__MODULE__, [], function: :run),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BasicSite.Supervisor]
Supervisor.start_link(children, opts)
end
def run do
{:ok, _} =Plug.Adapters.Cowboy.http(BasicSite.Router, [])
end
end

Here we use the Cowboy adapter, and tell it to use our plug. Empty list denotes that we can pass additional arguments. This should start cowboy powered HTTP web server on port 4000(default) when the Application is started. It’s important to remember that the Cowboy server will run various processes. There will be at least one process that listens on a given port and accepts requests. Then, each distinct TCP connection will be handled in a separate process, and our call-backs will be invoked in those request specific processes.

Router Module

Under lib create a folder basic_site and within it code router.ex as under:

lib/basic_site/router.ex

defmodule BasicSite.Router do
use Plug.Router# includes boilerplate supplied by the library
plug Plug.Logger
plug :match #sets up plugs
plug :dispatch
# specifies handlers for routes
#first we add a route to the root path
get "/" do 
send_resp(conn, 200, "Hi from plug")
end
# a smarter macro
get "/:name" do  # ----1)
send_resp(conn, 200, “Hi from #{name}”)
end
end

  1. The first macro just sends a response when the request is for root-path. The next macro is a bit smarter and returns a response based on the URL. Pre-pending part of a route with a colon creates a wildcard match. In this example :name will match anything and it will create a variable called name inside of the route. So if we access http://localhost:4000/Name, we should see “Hello, Name”.-or whatever we give for name.

The Plug.Router itself  is a plug and it contains its own plug pipeline and its use mixes in Plug's routing functions. The match macro, which is responsible for matching the request to one of our routes. Likewise, the dispatch macro is responsible for processing the matched route.. Plug ships with many plugs that you can add to the router plug pipeline, allowing you to plug something before a route matches or before a route is dispatched to. We have added logging to the router pipeline.

plug Plug.Logger
plug :match
plug :dispatch

Plug.Router compiles all of our routes into a single function and relies on the Erlang VM to optimize the underlying routes into a tree lookup, instead of a linear lookup that would instead match route-per-route. This means route lookups are extremely fast in Plug. Each route needs to return the connection as per the Plug specification.

  1. Notice that in the request handler, we use the conn variable, which doesn’t exist anywhere. This variable is provided by Plug and it is available inside of any route. Here it is brought to you by the get macro, which generates this variable and binds it to the proper value. As the name implies, the conn variable holds our connection. This is an instance of a Plug.Conn structure that holds our TCP socket, together with various information about the state of the request we’re processing. From our handler code, we must return the modified connection that will hold response information such as its status and body.

Run the app

Go to the command prompt and execute from the project folder

iex –S mix

the iex will start. Now go to your browser and navigate to localhost:4000. You will see Figure-2.if  you add Ganesh and navigate to localhost:4000/Ganesh you will see Figure -3.Instead of directly sending a string as response we can use a template to send the response. For that purpose the following modifications are required.

Stop the shell.

  1. Change the macro get “/:name” as under.

get "/:name" do
send_resp(conn, 200, EEx.eval_file("templates/name.eex", [name: name]))

  1. Create a folder templates under project root and the file name.eex under it.The code for name.eex is given below:

templates/name.eex

<h1>Hello, <%= name %></h1>

Now execute
iex –S mix
and navigate to localhost:4000/Venkat
You will see Figure-4.

How to send a JSON response

JSON reponses are the order of the day and to send them and the library “ poison” will be used which we added to our dependencies.Have a look at the get macro which we will add to the Routes.

# Send JSON response
get "/json/:name" do
conn
|> put_resp_content_type("application/json")  # Header is set
|> send_resp(200, Poison.encode!(%{name: name}))
End

Figure-2

Figure-3

Figure-4

Figure-5

Conclusion

Erlang is used much in mobile Networks.50% of world’s mobile Networks run on it. In messaging applications like “WhatsApp”, where the number of concurrent requests are numerous, it is the best choice. But in HTTP web applications, it has not made much headway, though it has got some good web-servers and frameworks. Elixir with its Ruby-like syntax, Lisp-macros and productivity tools, focuses on web-applications. Plug is a library created in that attempt. Like Ruby’s Rake or Clojure’s Ring, it takes care of nitty-gritty details of HTTP and provides higher level components for others to build upon. We can use those components and create basic web applications easily. Frameworks like Phoenix and Sugar have been built on top of Plug. Phoenix provides more functionalities like integration with Ecto models and has a web-socket implementation called Channels. The basic web application with Plug we created will give us a better understanding of how these frameworks work. In Plug or in web frameworks built on top of it, different requests are handled in different processes. This is how the underlying Cowboy web server is implemented. It uses one process to listen on a port, and then it spawns a separate process for each incoming request. This architecture has all sort of benefits due to the way BEAM treats processes. Because processes are concurrent, CPU resources are maximally used, and the system is scalable. Because processes are lightweight and isolated, we can easily manage a large number and we can be certain that occasional long-running, CPU-intensive requests won’t paralyze the entire system. Due to process isolation, a crash in a single request won’t affect the rest of the system.








}