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

Extending OTP with custom behaviours

Extending OTP with custom behaviours

Michał Muskała

December 03, 2016
Tweet

More Decks by Michał Muskała

Other Decks in Programming

Transcript

  1. http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ “Furthermore, we have already defined three implementations of the

    Twitter API, so we better make it all explicit. In Elixir we do so by defining a behaviour with callback functions (…)”
  2. defmodule Calculator do def start do spawn(__MODULE__, :loop, [[]]) end

    def add(pid, x, y) do send(pid, {:add, self(), x, y}) receive do {:result, x} -> x end end def loop(state) do receive do {:add, from, x, y} -> send(from, {:result, x + y}) end loop(state) end end
  3. MESSAGE HANDLER PROCESS • waits for a message • parses

    the message • handles the message • sends a reply
  4. defmodule Calculator do def start do spawn(__MODULE__, :loop, [[]]) end

    def add(pid, x, y) do send(pid, {:add, self(), x, y}) receive do {:result, x} -> x end end def loop(state) do receive do {:add, from, x, y} -> send(from, {:result, x + y}) end loop(state) end end
  5. MESSAGE HANDLER PROCESS • waits for a message • parses

    the message • handles the message • sends a reply
  6. defmodule Calculator do use GenServer def start do GenServer.start_link(__MODULE__, [])

    end def add(pid, x, y) do GenServer.call(pid, {:add, x, y}) end def handle_call({:add, x, y}, from, state) do {:reply, x + y, state} end end
  7. OTP SPECIAL PROCESSES • process fits into a supervision tree

    • support for the :sys debug utilities • process system messages • OTP design principles
 http://erlang.org/doc/design_principles/des_princ.html
  8. SPECIAL PROCESS IMPLEMENTATION • start with :proc_lib.start_link/3 • initialise debug

    with :sys.debug_options/1 • respond to start_link with :proc_lib.init_ack/2 • use :sys.handle_system_msg/6 for {:system, from, request} • implement system_continue/3 and system_terminate/4 callbacks
  9. DBCONNECTION • connect/1, disconnect/2, ping/1 • out of process state:

    checkout/1, checkin/1 • transaction: handle_begin/2, handle_commit/2, handle_rollback/2 • queries: handle_prepare/3, handle_execute/4, handle_close/3 • coursors: handle_declare/4, handle_first/4, handle_next/4, handle_deallocate/4
  10. ECTO.ADAPTER • ensure_all_started/2, child_spec/3 • loaders/2, dumpers/2, autogenerate/1 • prepare/2,

    execute/6, insert_all/7, insert/6, update/6, delete/4 • transaction/3, in_transaction?/1, rollback/2 • extensions for storage, migrations, dumps
  11. RANCH_TRANSPORT • acceptor: listen/1, accept/2, accept_ack/2 • connect/3, connect/4 •

    communication: recv/3, send/2, sendfile/2, sendfile/4, sendfile/5 • config: setopts/2, controlling_process/2, • read settings: name/0, secure/0, messages/0, peername/1, sockname/1 • shutdown/2, close/1
  12. GENSTAGE • init/1 • handle_demand/2 • handle_subscribe/4, handle_cancel/3 • handle_events/3

    • handle_call/3, handle_cast/2, handle_info/2, teminate/2, code_change/3
  13. defmacro __using__(_) do quote do use GenServer def handle_call({:foo, args},

    _, state) do {reply, state} = some_callback(args, state) Some.more_logic(reply, state) {:reply, reply, state} end defp some_callback(args, state), do: {:ok, state} defoverridable [some_callback: 2] end end
  14. use GenServer @callback init(term) :: {:ok, state :: term} |

    ... @callback some_callback(term) :: {reply :: term, state :: term} def start_link(module, args, opts) do GenServer.start_link(__MODULE__, {module, args, opts}, opts) end def init({mod, args, opts}) do case mod.init(args) do {:ok, int} -> %{mod: mod, int: int} other -> other end end
  15. def handle_call({:foo, arg}, _from, %{mod: mod, int: int} = state)

    do {reply, int} = mod.some_callback(arg, int) {:reply, reply, %{state | internal: int}} end def format_status(:normal, [pdict, %{mod: mod, int: int}]) do [{:data, [{'State', int}]}] end def format_status(:terminate, [pdict, %{int: int}]) do int end