Your browser doesn’t support the features needed to display this presentation.

A simplified version of this presentation follows.

For the best experience, please use one of the following browsers:

Web Scale Made Fun with Elixir and Phoenix


Memphis Ruby

7/9/2015

Josh W Lewis - @joshwlewis

Heroku


joshwlewis.com/slides/elixir_phoenix

Elixir is:

  • Inspired by Ruby
  • Functional
  • Concurrent
  • Fault Tolerant
  • Extensible

Heavily inspired by Ruby!

Ruby

module Food
  def eat(name)
    if name == "donut"
      puts "too fattening."
    else
      puts "nom nom."
    end
  end
end

Elixir

defmodule Food do
  def eat(name) do
    if name == "donut" do
      IO.puts "too fattening."
    else
      IO.puts "nom nom."
    end
  end
end

Elixir is Functional

Ruby

user = User.find(1)
user.name = "Bob"
user.save

Elixir

user = Repo.get(User, 1)
changes = User.changeset(user, %{name: "Bob"})
Repo.insert(changes)

Elixir is Concurrent

defmodule Fib do
  def num(1), do: 0
  def num(2), do: 1
  def num(n) do
    two_back = Task.async(fn -> Fib.num(n-2) end)
    one_back = Task.async(fn -> Fib.num(n-1) end)
    Task.await(two_back) + Task.await(one_back)
  end
end

Fib.number(1)  # => 0
Fib.number(5)  # => 3
Fib.number(20) # => 4181

Elixir is Fault Tolerant

# mix.exs
defmodule HexWeb.Mixfile do
  use Mix.Project

  def project do
    [app: :hex_web,
     version: "0.0.1",
     elixir: "~> 1.0",
     ...
     deps: deps]
  end

  def application do
    [applications: [:logger, :plug, :cowboy, :ecto, :postgrex],
     mod: {HexWeb, []},
     env: []]
  end

  defp deps do
    [{:plug,      "~> 0.11"},
     {:cowboy,    "~> 1.0"},
     {:ecto,      github: "elixir-lang/ecto"},
     {:postgrex,  ">= 0.0.0"},
     ...
   ]
  end
end

Elixir is Extensible

defmodule Fib do
  Enum.with_index([0,1,1,2,3,5,8,11])
  |> Enum.each fn {num, index} ->
    def num(unquote(index + 1)), do: unquote(num)
  end

  def num(n), do: num(n-1) + num(n-2)
end

Fib.num(1)  # => 1
Fib.num(5)  # => 3
Fib.num(20) # => 4181

Elixir is Backed By Erlang

:crypto.md5("Using crypto from Erlang OTP")

Elixir has Cool Tools

Phoenix Framework

Phoenix's Model Comes From Ecto

defmodule MyCoolApp.User do
  use MyCoolApp.Web, :model

  schema "users" do
    field :email,              :string
    field :password,           :string

    timestamps
  end

  @required_fields ~w(email password)

  def changeset(user, params) do
    cast(user, params, ~w(email password), [])
    |> update_change(:email, &String.downcase/1)
    |> validate_format(:email, ~r"^.+@.+\..+$")
  end
end

Phoenix's Router Looks Familiar

defmodule MyCoolApp.Router do
  use MyCoolApp.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", MyCoolApp do
    pipe_through :browser

    get "/", PageController, :index

    get "/login", LoginController, :new
    post "/login", LoginController, :create

    resources "/users", UserController
  end
end

Phoenix's Controllers Familiar but Functional

defmodule MyCoolApp.LoginController do
  use MyCoolApp.Web, :controller
  alias MyCoolApp.Login

  def new(conn, _params) do
    render(conn, :new, login: %Login{})
  end

  def create(conn, %{"login" => %{"email" => email, "password" => password}}) do
    login = %Login{email: email, password: password}

    if user = Login.authorized_user(login) do
      put_session(conn, :user_id, user.id)
      |> redirect(to: users_path(conn, :index))
    else
      put_flash(conn, :error, "Invalid credentials provided.")
      |> render(:new, login: login)
    end
  end

Phoenix Has a View Layer

It sits between the Controller and Template.

defmodule MyCoolApp.UserView do
  use MyCoolApp.Web, :view

  def render("show.json", %{user: user}) do
    %{data: render_one(user, "user.json")}
  end

  def render("user.json", %{user: user}) do
    %{
      type: "user",
      id:   user.id,
      attributes: %{
        email:       user.email,
        inserted_at: user.inserted_at,
        updated_at:  user.updated_at
      }
    }
  end
end

.eex Templates are Also Familiar

<%= form_for @conn, login_path(@conn, :create), [name: :login], fn f -> %>
  <div class="form-group">
    <label>Email</label>
    <%= email_input f, :email, class: "form-control" %>
  </div>
  <div class="form-group">
    <label>Password</label>
    <%= password_input f, :password, class: "form-control" %>
  </div>
  <div class="form-group">
    <%= submit "Submit", class: "btn btn-primary" %>
  </div>
<% end %>

Demo Time

Resources

This Presentation
joshwlewis.com/slides/elixir_phoenix
Elixir Language
elixir-lang.org
Phoenix Framework
phoenixframework.org