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

Extending OTP with custom behaviours

Extending OTP with custom behaviours

Avatar for Michał Muskała

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