$30 off During Our Annual Pro Sale. View Details »

ElixirConf 2016 - From Front-End to Full Stack with Elixir & Phoenix

Lauren Tan
September 02, 2016

ElixirConf 2016 - From Front-End to Full Stack with Elixir & Phoenix

This talk was presented at ElixirConf 2016.

Video: https://www.youtube.com/watch?v=r4ulu8wo_GI

Starting out as a self-taught designer, learning front end development was a challenging climb – but going from front end to full stack with Elixir and Phoenix was easier than expected. Having built a well-tested Phoenix API from the ground up, I'll narrate the adventure of learning to think "the Elixir Way", and reflect on lessons learned.

In this beginner-friendly talk, we'll first talk about Elixir - harnessing the power of plugs, macros, pattern matching and recursion. Then we'll look at how Phoenix expands upon Elixir's philosophies to deliver a maintainable and performant web framework, including pipelines, techniques for testing, and more! Finally, we'll look at how we can avoid bringing in OOP concepts into a functional world.

Lauren Tan

September 02, 2016
Tweet

More Decks by Lauren Tan

Other Decks in Programming

Transcript

  1. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    From Front-End
    to Full Stack
    With Elixir & Phoenix

    View Slide

  2. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    bit.ly/front-end-to-full-stack

    View Slide

  3. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    LAUREN TAN

    View Slide

  4. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  5. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  6. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  7. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    ! SUGARPIRATE_
    " POTETO

    View Slide

  8. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    ZELDA

    View Slide

  9. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  10. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    A long time ago in a galaxy
    far, far away….

    View Slide

  11. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  12. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  13. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  14. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  15. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  16. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    JavaScript

    View Slide

  17. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    let foo = [1, 2, 3, 4, 5];
    let bar = foo.map((n) => n + 1); // [2, 3, 4, 5, 6]
    let baz = bar.filter((n) => n >=3 && n <= 5); // [3, 4, 5]
    let qux = baz.reduce((n, el) => n += el); // 12
    console.log(foo); // [1, 2, 3, 4, 5]

    View Slide

  18. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    let f = R.pipe(Math.pow, R.negate, R.inc);
    f(3, 4); // -(3^4) + 1

    View Slide

  19. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  20. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  21. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    MVC

    View Slide

  22. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    ember-changeset
    https://github.com/DockYard/ember-changeset

    View Slide

  23. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    {{dummy-form
    changeset=(changeset user EmployeeValidations)
    submit=(action "submit")
    rollback=(action "rollback")
    }}

    View Slide

  24. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    https://facebook.github.io/react

    View Slide

  25. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    http://redux.js.org/

    View Slide

  26. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  27. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  28. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    Elixir

    View Slide

  29. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  30. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    https://www.youtube.com/watch?v=rRbY3TMUcgQ

    View Slide

  31. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    Domain Logic
    Domain Logic
    Domain Logic
    Domain Logic
    Web Umbrella App

    View Slide

  32. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix
    https://www.manning.com/books/elixir-in-action
    https://www.manning.com/books/the-little-elixir-and-otp-guidebook

    View Slide

  33. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    |>i|>e

    View Slide

  34. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    foo(bar(baz(123), 456), 789)

    View Slide

  35. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    123
    |> baz()
    |> bar(456)
    |> foo(123)

    View Slide

  36. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Sugar?

    View Slide

  37. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Small Functions

    View Slide

  38. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    def serialize(user) do
    user
    |> serialize_attributes()
    |> serialize_relationships()
    |> to_json()
    end

    View Slide

  39. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    def validate_presence(input) do
    if input == "" do
    "Can't be blank"
    end
    input
    end

    View Slide

  40. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Pattern Matching

    View Slide

  41. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    def validate_presence(""), do: {:err, "Can't be blank"}
    def validate_presence(input), do: input

    View Slide

  42. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyPlug do
    import Plug.Conn
    def init(opts), do: opts
    def call(conn, currency: currency) do
    case currency do
    "USD" -> assign(conn, :greeting, "murica")
    "CAD" -> assign(conn, :greeting, "eh?")
    "AUD" -> assign(conn, :greeting, "oi mate")
    _ -> conn
    end
    end
    end

    View Slide

  43. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyPlug do
    import Plug.Conn
    def init(opts), do: opts
    def call(conn, currency: currency) do
    greet(conn, currency)
    end
    def greet(conn, "USD"), do: assign(conn, :greeting, "murica")
    def greet(conn, "CAD"), do: assign(conn, :greeting, "eh?")
    def greet(conn, "AUD"), do: assign(conn, :greeting, "oi mate")
    def greet(conn, _), do: conn
    end

    View Slide

  44. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule WebhookMailer do
    use Phoenix.Swoosh, view: MyApp.EmailView, layout: {MyApp.LayoutView, :email}
    alias MyApp.User
    def webhook(topic, %User{first_name: first_name, last_name: last_name, email: email}) do
    title = title_for(topic)
    new
    |> to({"#{first_name} #{last_name}", email})
    |> from({"Patrick", "[email protected]"})
    |> subject(title)
    |> render_body("webhook.html", %{title: title, first_name: first_name})
    end
    # ...
    end

    View Slide

  45. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defp title_for("customer_created"),
    do: "Created account successfully"
    defp title_for("customer_transfer_created"),
    do: "Transfer created"
    defp title_for("customer_transfer_cancelled"),
    do: "Transfer cancelled"
    defp title_for("customer_transfer_failed"),
    do: "Transfer failed"
    defp title_for("customer_transfer_completed"),
    do: "Transfer completed"
    defp title_for("customer_funding_source_added"),
    do: "Funding source added"
    defp title_for("customer_funding_source_removed"),
    do: "Funding source removed"
    defp title_for("customer_verified"),
    do: "Your account was verified"
    defp title_for("customer_suspended"),
    do: "Your account was suspended"
    defp title_for("customer_verification_documentation_needed"),
    do: "Documentation needed"
    defp title_for("customer_verification_documentation_uploaded"),
    do: "Documentation uploaded"
    defp title_for("customer_verification_documentation_approved"),
    do: "Documentation approved"
    defp title_for(message), do: message

    View Slide

  46. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Recursion

    View Slide

  47. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    {
    "id": "1",
    "foo": {
    "bar": {
    "qux": "hello world"
    },
    "baz": 123
    }
    }

    View Slide

  48. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Json do
    def flatten(%{} = json) do
    json
    |> Map.to_list() # [foo: %{bar: %{qux: "hello world"}, baz: 123}, id: "1"]
    |> to_flat_map(%{})
    end
    def flatten(%{} = json) when json == %{}, do: %{}
    # ...
    end

    View Slide

  49. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    bae's case

    View Slide

  50. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defp to_flat_map([], acc), do: acc

    View Slide

  51. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Add to accumulator

    View Slide

  52. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defp to_flat_map([{_k, %{} = v} | t], acc) do
    v
    |> Map.to_list()
    |> to_flat_map(to_flat_map(t, acc))
    end

    View Slide

  53. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Flatten all the things

    View Slide

  54. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defp to_flat_map([{k, v} | t], acc) do
    to_flat_map(t, Map.put_new(acc, k, v))
    end

    View Slide

  55. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    %{id: "1", foo: %{bar: %{qux: "hello world"}, baz: 123}}
    |> Json.flatten()
    |> IO.inspect()
    # %{baz: 123, id: "1", qux: "hello world"}

    View Slide

  56. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Json do
    def flatten(%{} = json) when json == %{}, do: %{}
    def flatten(%{} = json) do
    json
    |> Map.to_list()
    |> to_flat_map(%{})
    end
    defp to_flat_map([{_k, %{} = v} | t], acc), do: to_flat_map(Map.to_list(v), to_flat_map(t, acc))
    defp to_flat_map([{k, v} | t], acc), do: to_flat_map(t, Map.put_new(acc, k, v))
    defp to_flat_map([], acc), do: acc
    end

    View Slide

  57. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Data Types
    https://engineering.appcues.com/2016/02/02/too-many-dicts.html

    View Slide

  58. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Maps

    View Slide

  59. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    %{
    "data" => %{
    "type" => "articles",
    "id" => "1"
    }
    }

    View Slide

  60. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  61. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    %{
    id: "1",
    name: "Bob"
    }

    View Slide

  62. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Structs

    View Slide

  63. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Employee do
    @enforce_keys [:id]
    defstruct id: nil, name: nil
    @type t :: %Employee{id: pos_integer, name: String.t}
    end
    defmodule Team do
    defstruct name: nil, employees: []
    @type t :: %Team{name: String.t, employees: [Employee.t, ...]}
    end

    View Slide

  64. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Employee do
    # ...
    def promote(%Employee{salary: salary} = employee, pay_rise) do
    %{employee | salary: round(salary * (1 + pct_increase))}
    end
    end
    defmodule Team do
    # ...
    def promote_all(%Team{employees: employees}, bonus) do
    Enum.map(employees, &(Employee.promote(&1, bonus)))
    end
    end

    View Slide

  65. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    bob = %Employee{id: "1", name: "Bob", salary: 80_000}
    jane = %Employee{id: "2", name: "Jane", salary: 100_000}
    team = %Team{name: "Accounting", employees: [bob, jane]}
    # %Team{employees: [%Employee{id: "1", name: "Bob", salary: 80000},
    # %Employee{id: "2", name: "Jane", salary: 100000}], name: "Accounting"}
    Team.promote_all(team, 0.2) # 20% pay rise
    # [%Employee{id: "1", name: "Bob", salary: 96000},
    # %Employee{id: "2", name: "Jane", salary: 120000}]

    View Slide

  66. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Tuples

    View Slide

  67. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    {:ok, result}

    View Slide

  68. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    {:ok, result}
    {:err, "message"}

    View Slide

  69. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Foo do
    def serialize(user) do
    with {:ok, serialized} <- serialize_attributes(user),
    {:ok, serialized} <- serialize_relationships(serialized),
    do: to_json(serialized)
    end
    def serialize_attributes(user), do: {:ok, user}
    def serialize_relationships(user), do: {:ok, user}
    def to_json(map), do: Poison.encode!(map)
    end

    View Slide

  70. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Keywords

    View Slide

  71. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    def to_currency(value, currency: "USD", cents?: cents?), do: # ...

    View Slide

  72. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Protocols

    View Slide

  73. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  74. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Team do
    # ...
    defimpl Enumerable do
    def count(%Team{employees: employees}), do: {:ok, length(employees)}
    end
    end

    View Slide

  75. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    team
    # %Team{employees: [%Employee{id: "1", name: "Bob", salary: 80000},
    # %Employee{id: "2", name: "Jane", salary: 100000}], name: "Accounting"}
    Enum.count(team)
    # 2

    View Slide

  76. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Team do
    # ...
    defimpl Enumerable do
    def count(%Team{employees: employees}), do: {:ok, length(employees)}
    def member?(_, _), do: {:error, __MODULE__}
    def reduce(%Team{employees: employees}, acc, fun), do: do_reduce(employees, acc, fun)
    # default list reducer
    defp do_reduce(_, {:halt, acc}, _fun), do: {:halted, acc}
    defp do_reduce(list, {:suspend, acc}, fun), do: {:suspended, acc, &do_reduce(list, &1, fun)}
    defp do_reduce([], {:cont, acc}, _fun), do: {:done, acc}
    defp do_reduce([h | t], {:cont, acc}, fun), do: do_reduce(t, fun.(h, acc), fun)
    end
    end

    View Slide

  77. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    bob = %Employee{id: "1", name: "Bob", salary: 10_000}
    jane = %Employee{id: "2", name: "Jane", salary: 20_000}
    team = %Team{name: "Accounting", employees: [bob, jane]}
    Enum.member?(team, bob)
    # true
    Enum.map(team, fn e -> e.name end)
    # ["Bob", "Jane"]
    Enum.max_by(team, fn e -> e.salary end)
    # %Employee{id: "2", name: "Jane", salary: 20000}

    View Slide

  78. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Team do
    # ...
    def promote_all(%Team{employees: employees}, bonus) do
    Enum.map(employees, &(Employee.promote(&1, bonus)))
    end
    end

    View Slide

  79. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Team do
    # ...
    def promote_all(%Team{} = team, bonus) do
    Enum.map(team, &(Employee.promote(&1, bonus)))
    end
    end

    View Slide

  80. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Behaviours

    View Slide

  81. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Interface

    View Slide

  82. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Request do
    defstruct headers: %{}, body: %{}
    end

    View Slide

  83. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Validator do
    @callback validate(request :: Request.t, attribute :: :atom) :: %{}
    @callback valid?(value :: any) :: boolean
    @optional_callbacks valid?: 1
    end

    View Slide

  84. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Validator.Presence do
    @behaviour Validator
    def validate(%Request{body: body}, attribute) do
    valid?(Map.get(body, attribute))
    end
    def valid?(nil), do: false
    def valid?(%{} = map) when map === %{}, do: false
    def valid?([]), do: false
    def valid?(_), do: true
    end

    View Slide

  85. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    warning: undefined behaviour function validate/2 (for behaviour Validator)
    lib/examples/behaviours.ex:10
    warning: undefined behaviour function valid?/1 (for behaviour Validator)
    lib/examples/behaviours.ex:10

    View Slide

  86. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    # behaviours.ex:15: The call 'Elixir.Map':get(body@1::[],attribute@1::any()) will
    # never return since the success typing is (map(),any()) -> any()
    # and the contract is (map(),key()) -> value()

    View Slide

  87. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    $ mix dialyzer
    https://github.com/jeremyjh/dialyxir

    View Slide

  88. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    @spec
    https://medium.com/@barruumrex/seeking-simple-satisfaction-2a098902ddff

    View Slide

  89. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Macros

    View Slide

  90. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  91. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Elixir is written in Elixir

    View Slide

  92. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  93. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  94. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    elixir/lib/elixir/src/elixir_bootstrap.erl

    View Slide

  95. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    # Use elixir_bootstrap module to be able to bootstrap Kernel.
    # The bootstrap module provides simpler implementations of the
    # functions removed, simple enough to bootstrap.
    import Kernel, except: [@: 1, defmodule: 2, def: 1, def: 2, defp: 2,
    defmacro: 1, defmacro: 2, defmacrop: 2]
    import :elixir_bootstrap
    defmodule Kernel do
    # ...
    end

    View Slide

  96. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Erlang compiles Elixir

    View Slide

  97. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    -export(['MACRO-def'/2, 'MACRO-def'/3, 'MACRO-defp'/3, 'MACRO-defmodule'/3,
    'MACRO-defmacro'/2, 'MACRO-defmacro'/3, 'MACRO-defmacrop'/3,
    'MACRO-@'/2, '__info__'/1]).

    View Slide

  98. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  99. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Kernel.SpecialForms

    View Slide

  100. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmacro unquote(:%{})(args), do: error!([args])

    View Slide

  101. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    expand({'%{}', Meta, Args}, E) ->
    elixir_map:expand_map(Meta, Args, E);

    View Slide

  102. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    expand_map(Meta, [{'|', UpdateMeta, [Left, Right]}], E) ->
    {[ELeft | ERight], EA} = elixir_exp:expand_args([Left | Right], E),
    validate_kv(Meta, ERight, Right, E),
    {{'%{}', Meta, [{'|', UpdateMeta, [ELeft, ERight]}]}, EA};
    expand_map(Meta, Args, E) ->
    {EArgs, EA} = elixir_exp:expand_args(Args, E),
    validate_kv(Meta, EArgs, Args, E),
    {{'%{}', Meta, EArgs}, EA}.

    View Slide

  103. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    :math.pi
    # 3.141592653589793

    View Slide

  104. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    :maps.new() === %{}
    # true

    View Slide

  105. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule CowSay do
    defmacro __using__(_) do
    quote do
    def cowsay(str) do
    """
    #{make_line("_", String.length(str))}
    < #{str} >
    #{make_line("-", String.length(str))}
    \\ ^__^
    \\ (oo)\\_______
    (__)\\ )\\/\\
    ||----w |
    || ||
    """
    |> IO.puts()
    end
    defp make_line(char, len), do: String.duplicate(char, len + 1)
    end
    end
    end

    View Slide

  106. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Bob do
    use CowSay
    end
    Bob.cowsay("It's me Bob")

    View Slide

  107. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  108. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    https://pragprog.com/book/cmelixir/metaprogramming-elixir

    View Slide

  109. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  110. ElixirConf 2016
    From Front-End to Full Stack with Elixir & Phoenix

    View Slide

  111. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  112. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Pipelines

    View Slide

  113. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    lib/my_app/endpoint.ex

    View Slide

  114. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    conn

    View Slide

  115. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    conn

    View Slide

  116. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    conn

    View Slide

  117. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    conn

    View Slide

  118. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    conn
    conn
    Endpoint
    Router Controller
    DB
    Pipeline
    200 OK
    Ecto
    View

    View Slide

  119. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    def phoenix(conn, _opts) do
    conn
    |> endpoint()
    |> router()
    |> pipeline()
    |> controller()
    |> view()
    end

    View Slide

  120. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Phoenix is a pipeline

    View Slide

  121. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Composed of pipelines

    View Slide

  122. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Made out of plugs

    View Slide

  123. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    And functions

    View Slide

  124. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  125. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Plug

    View Slide

  126. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  127. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    def my_plug(conn, _opts) do
    conn
    end

    View Slide

  128. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.Authorization do
    import Plug.Conn
    def init(opts), do: opts
    def call(conn, opts) do
    # ...
    end
    end

    View Slide

  129. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.Router do
    use MyApp.Web, :router
    pipeline :authorized do
    plug MyApp.Authorization
    end
    scope "/api", MyApp do
    pipe_through :authorized
    scope "/v1", Api.V1 do
    get "/status", StatusController, :index
    end
    end
    end

    View Slide

  130. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.WebhookController do
    use MyApp.Web, :controller
    plug :verify_request_signature
    def create(conn, # ...) do
    # ...
    end
    defp verify_request_signature(conn, _) do
    # ...
    end
    end

    View Slide

  131. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    pipeline :json_api do
    plug :accepts, ["json", "json-api"]
    plug :fetch_session
    plug JaSerializer.ContentTypeNegotiation
    plug JaSerializer.Deserializer
    end

    View Slide

  132. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    scope "/api", MyApp do
    pipe_through :json_api
    # public routes
    get "/foo", FooController, :index
    scope "/v1", Api.V1 do
    pipe_through :authorized
    resources "/users", UserController: only: [:index, :show]
    end
    end

    View Slide

  133. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    lib/my_app/endpoint.ex

    View Slide

  134. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.Endpoint do
    use Phoenix.Endpoint, otp_app: :my_app
    socket "/socket", MyApp.UserSocket
    plug Plug.Static,
    at: "/", from: :my_app, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)
    if code_reloading? do
    plug Phoenix.CodeReloader
    end
    plug Plug.RequestId
    plug Plug.Logger
    plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Poison
    plug Plug.MethodOverride
    plug Plug.Head
    plug Plug.Session,
    store: :cookie,
    key: "_my_app_key",
    signing_salt: "BKi/ZZMd"
    plug MyApp.Router
    end

    View Slide

  135. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Fractal

    View Slide

  136. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Terraform
    https://github.com/poteto/terraform

    View Slide

  137. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Incremental Phoenix

    View Slide

  138. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    iex(1)> [info] GET /v1/foo
    [debug] Processing by MyApp.FooController.index/2
    Parameters: %{}
    Pipelines: [:api]
    [info] Sent 200 in 22ms
    [info] GET /v1/bar
    [debug] Processing by MyApp.BarController.index/2
    Parameters: %{}
    Pipelines: [:api]
    [info] Sent 200 in 116µs
    [info] GET /v1/hello-world # forwarded to legacy API
    [info] Sent 200 in 1ms
    [info] GET /v1/gifs/search # forwarded to legacy API
    [info] Sent 200 in 102ms

    View Slide

  139. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.Endpoint do
    # ...
    plug MyApp.ForwardRequest,
    client: MyApp.Forwarders.Giphy,
    router: MyApp.Router
    plug MyApp.Router
    end

    View Slide

  140. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.ForwardRequest do
    import Plug.Conn
    def init(opts), do: opts
    def call(conn, client: client, router: router) do
    routes = router.__routes__ #
    conn
    |> match_route(routes)
    |> handle_request(client)
    |> send_response()
    end
    # ...
    end

    View Slide

  141. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  142. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  143. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmacro __before_compile__(_env) do
    quote location: :keep do
    defoverridable [call: 2]
    def call(conn, opts) do
    try do
    super(conn, opts)
    catch
    kind, reason ->
    Plug.ErrorHandler.__catch__(conn, kind, reason, &handle_errors/2)
    end
    end
    end
    end

    View Slide

  144. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    catch
    kind, reason ->
    # do our forward here
    end

    View Slide

  145. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Terraform
    https://github.com/poteto/terraform

    View Slide

  146. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule Terraform do
    defmacro __using__(opts) do
    quote location: :keep do
    Module.register_attribute __MODULE__, :terraformer, []
    @terraformer Keyword.get(unquote(opts), :terraformer)
    @before_compile Terraform
    end
    end
    # ...
    end

    View Slide

  147. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmacro __before_compile__(_env) do
    quote location: :keep do
    defoverridable [call: 2]
    def call(conn, opts) do
    try do
    super(conn, opts)
    catch
    _, %{conn: conn} -> terraform(conn, @terraformer)
    end
    end
    defp terraform(conn, terraformer) do
    terraformer.call(conn, [])
    end
    defoverridable [terraform: 2]
    end
    end

    View Slide

  148. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.Router do
    use Terraform, terraformer: MyApp.Terraformers.Foo
    # ...
    end

    View Slide

  149. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    defmodule MyApp.Terraformers.Foo do
    alias MyApp.Clients.Foo # example client made with HTTPoison
    use Plug.Router
    plug :match
    plug :dispatch
    # match specific path
    get "/v1/hello-world", do: send_resp(conn, 200, "Hello world")
    # match all `get`s
    get _ do
    %{method: "GET", request_path: request_path, params: params, req_headers: req_headers} = conn
    res = Foo.get!(request_path, req_headers, [params: Map.to_list(params)])
    send_response({:ok, conn, res})
    end
    def send_response({:ok, conn, %{headers: headers, status_code: status_code, body: body}}) do
    conn = %{conn | resp_headers: headers}
    send_resp(conn, status_code, body)
    end
    end

    View Slide

  150. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  151. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  152. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Design Patterns

    View Slide

  153. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  154. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  155. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  156. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    –Sandi Metz
    “Duplication is far cheaper than
    the wrong abstraction.”

    View Slide

  157. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  158. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    State

    View Slide

  159. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Objects

    View Slide

  160. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    class Person {
    constructor(name) {
    this.name = name;
    this.x = 0;
    this.y = 0;
    }
    greet() {
    return `Hi there, my name is ${this.name}.`;
    }
    report() {
    return `I'm at (${this.x}, ${this.y}).`;
    }
    move(x = 0, y = 0) {
    this.x = x;
    this.y = y;
    }
    }

    View Slide

  161. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    let bob = new Person('Bob');
    bob.greet(); // "Hi there, my name is Bob."
    bob.report(); // "I'm at (0, 0)."

    View Slide

  162. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    bob.move(1, 2);
    bob.report(); // "I'm at (1, 2)."

    View Slide

  163. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    class Movable {
    constructor(x, y) {
    this.move(x, y);
    }
    move(x = 0, y = 0) {
    this.x = x;
    this.y = y;
    }
    report() {
    return `I'm at (${this.x}, ${this.y}).`;
    }
    }

    View Slide

  164. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    class Person extends Movable {
    constructor(name) {
    super();
    this.name = name;
    }
    greet() {
    return `Hi there, my name is ${this.name}.`;
    }
    }

    View Slide

  165. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Data

    View Slide

  166. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Function

    apple -> banana

    View Slide

  167. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    .toBanana();

    View Slide

  168. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    .toPineapple();

    View Slide

  169. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Uncaught TypeError: .toPineapple is not a function(…)

    View Slide

  170. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  171. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    Function

    apple -> banana
    Function
    banana -> pineapple

    View Slide

  172. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    Function

    apple -> banana
    Function
    banana -> pineapple

    Composition

    View Slide

  173. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Function

    apple -> pineapple

    View Slide

  174. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Model the System

    View Slide

  175. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016

    View Slide

  176. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Transform the Data
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  177. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Fractal

    View Slide

  178. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Function
    input output
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  179. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    low level operation low level operation low level operation
    EmailValidator
    Service
    email validated result
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  180. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    service service service
    UpdateProfile
    Use-case
    change profile
    request
    change profile
    result
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  181. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    use-case use-case use-case
    Web Application
    http request http response
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  182. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Web Application
    conn conn
    https://fsharpforfunandprofit.com/fppatterns/

    View Slide

  183. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    tl;dr

    View Slide

  184. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  185. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  186. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    I. Learn You Some Elixir for Great Good!
    II. Phoenix is Not Rails
    III.OOPs

    View Slide

  187. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    LAUREN TAN
    ! SUGARPIRATE_
    " POTETO

    View Slide

  188. From Front-End to Full Stack with Elixir & Phoenix
    ElixirConf 2016
    Thanks!

    View Slide