Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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
  2. ElixirConf 2016 From Front-End to Full Stack with Elixir &

    Phoenix A long time ago in a galaxy far, far away….
  3. 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]
  4. 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
  5. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 {{dummy-form changeset=(changeset user EmployeeValidations) submit=(action "submit") rollback=(action "rollback") }}
  7. 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
  8. 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
  9. ElixirConf 2016 From Front-End to Full Stack with Elixir &

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

    Phoenix Domain Logic Domain Logic Domain Logic Domain Logic Web Umbrella App
  11. 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
  12. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 def serialize(user) do user |> serialize_attributes() |> serialize_relationships() |> to_json() end
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 { "id": "1", "foo": { "bar": { "qux": "hello world" }, "baz": 123 } }
  21. 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
  22. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 defp to_flat_map([], acc), do: acc
  23. 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
  24. 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
  25. 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"}
  26. 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
  27. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 %{ "data" => %{ "type" => "articles", "id" => "1" } }
  29. 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
  30. 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
  31. 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}]
  32. 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
  33. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 def to_currency(value, currency: "USD", cents?: cents?), do: # ...
  34. 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
  35. 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
  36. 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
  37. 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}
  38. 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
  39. 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
  40. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 defmodule Request do defstruct headers: %{}, body: %{} end
  41. 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
  42. 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
  43. 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
  44. 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()
  45. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

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

    2016 elixir/lib/elixir/src/elixir_bootstrap.erl
  48. 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
  49. 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]).
  50. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 expand({'%{}', Meta, Args}, E) -> elixir_map:expand_map(Meta, Args, E);
  52. 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}.
  53. 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
  54. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 https://pragprog.com/book/cmelixir/metaprogramming-elixir
  56. 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
  57. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

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

    2016 def my_plug(conn, _opts) do conn end
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 Terraform https://github.com/poteto/terraform
  67. 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
  68. 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
  69. 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
  70. 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
  71. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 Terraform https://github.com/poteto/terraform
  73. 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
  74. 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
  75. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 defmodule MyApp.Router do use Terraform, terraformer: MyApp.Terraformers.Foo # ... end
  76. 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
  77. 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
  78. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 https://fsharpforfunandprofit.com/fppatterns/
  80. 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; } }
  81. 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)."
  82. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 bob.move(1, 2); bob.report(); // "I'm at (1, 2)."
  83. 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}).`; } }
  84. 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}.`; } }
  85. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 Function apple -> banana Function banana -> pineapple
  87. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 Function apple -> banana Function banana -> pineapple Composition
  88. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

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

    2016 Function input output https://fsharpforfunandprofit.com/fppatterns/
  90. 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/
  91. 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/
  92. 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/
  93. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 Web Application conn conn https://fsharpforfunandprofit.com/fppatterns/
  94. 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
  95. 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
  96. 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
  97. From Front-End to Full Stack with Elixir & Phoenix ElixirConf

    2016 LAUREN TAN ! SUGARPIRATE_ " POTETO