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

Spreading my love for Elixir and State Machines

Spreading my love for Elixir and State Machines

98195776df79590269541395c699f816?s=128

João Moura

May 02, 2018
Tweet

Transcript

  1. Spreading myElixir Elixir love

  2. None
  3. This shit is easy

  4. None
  5. None
  6. things can get things can get complex complex really fast

    really fast
  7. State Machines State Machines

  8. empty Shopping Cart Shopping Cart filled payed abandoned add item

    leave checkout
  9. empty lock items add item in stock? no yes filled

    checkout leave payed? no payed yes abandoned unblock items
  10. Models Models Controllers Controllers Views Views

  11. Functional Functional Programming Programming

  12. Functions must not Functions must not update values outside of

    it update values outside of it
  13. Functions must not Functions must not depend on variables other

    depend on variables other than its parameters than its parameters
  14. "Elixir".downcase # elixir

  15. String.downcase("Elixir") # elixir

  16. Machinery Machinery

  17. defmodule YourProject.UserStateMachine do use Machinery, states: ["created", "partial", "complete"], transitions:

    %{ "created" => ["partial", "complete"], "partial" => "completed" } end
  18. defmodule YourProject.UserStateMachine do use Machinery, states: ["created", "partial", "complete"], transitions:

    %{ "created" => ["partial", "complete"], "partial" => "completed" } end
  19. defmodule YourProject.UserStateMachine do use Machinery, states: ["created", "partial", "complete"], transitions:

    %{ "created" => ["partial", "complete"], "partial" => "completed" } end
  20. defmodule YourProject.UserStateMachine do use Machinery, states: ["created", "partial", "complete"], transitions:

    %{ "created" => ["partial", "complete"], "partial" => "completed" } end
  21. defmodule YourProject.UserStateMachine do use Machinery, states: ["created", "partial", "complete"], transitions:

    %{ "created" => ["partial", "complete"], "partial" => "completed" } end
  22. Machinery.transition_to(your_struct, UserStateMachine, "next_state") # {:ok, updated_struct}

  23. Machinery.transition_to(your_struct, UserStateMachine, "next_state") # {:ok, updated_struct}

  24. Machinery.transition_to(your_struct, UserStateMachine, "next_state") # {:ok, updated_struct}

  25. Machinery.transition_to(your_struct, UserStateMachine, "next_state") # {:ok, updated_struct}

  26. Guard Functions Guard Functions def guard_transition(struct, "complete") do Map.get(struct, :missing_fields)

    == false end
  27. Guard Functions Guard Functions def guard_transition(struct, "complete") do Map.get(struct, :missing_fields)

    == false end
  28. Guard Functions Guard Functions def guard_transition(struct, "complete") do Map.get(struct, :missing_fields)

    == false end def guard_transition(struct, "partial") do # ... end
  29. Before and after callbacks Before and after callbacks def before_transition(struct,

    "complete"), do: struct def after_transition(struct, "complete"), do: struct
  30. Shopping Cart Shopping Cart

  31. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  32. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  33. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  34. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  35. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  36. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  37. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  38. defmodule FakeProject.ShoppingCartMachine do # We start by declaring all states

    and # each permitted transition. use Machinery, states: ["empty", "filled", "payed", "abandoned"], transitions: %{ "empty" => "filled", "filled" => ["payed", "abandoned"] } def guard_function(cart, "filled") do Item.has_stock?(cart.item) end def guard_function(cart, "payed") do Payment.status(cart) == :confirmed end def before_transition(cart, "filled") do Item.lock_form_cart(cart) cart end def after_transition(cart, "abadonned") do Item.unlock_form_cart(cart) cart end end
  39. BONUS BONUS

  40. defmodule YourApp.Endpoint do # ... plug Machinery.Plug # ... end

    config :machinery, interface: true, repo: YourApp.Repo, model: YourApp.User, module: YourApp.UserStateMachine
  41. None
  42. None
  43. state machines state machines are hard are hard

  44. machinery machinery

  45. things can get things can get complex complex really fast

    really fast
  46. @joaomdmoura @joaomdmoura joaomdmoura.com