ヽ(´・肉・`)ノログ

How do we fighting without fighting?

すごいE本をElixirでやる(44)

第14章 OTPの紹介 - 14.2 基本的なサーバ - 開始関数 から

第14章OTPの紹介

14.2 基本的なサーバ

開始関数

開始関数(start、 start_link、init)の実装

defmodule MyServer do
  ### Public API
  def start(module, initial_state), do: spawn(fn -> init(module, initial_state) end)
  def start_link(module, initial_state), do: spawn_link(fn -> init(module, initial_state) end)

  def call(pid, msg) do
    ref = Process.monitor(pid)
    send(pid, {:sync, self, ref, msg})
    receive do
      {ref, reply} ->
        Process.demonitor(ref, [:flush])
        reply
      {:DOWN, ref, :process, pid, reason} ->
        raise(reason)
    after 5000 ->
      raise(:timeout)
    end
  end

  def cast(pid, msg) do
    send(pid, {:async, msg})
    :ok
  end

  def reply({pid, ref}, reply) do
    send(pid, {ref, reply})
  end

  ### Private stuff
  defp init(module, initial_state), do: loop(module, module.init(initial_state))

  defp loop(module, state) do
    receive do
      {:async, msg} ->
        loop(module, module.handle_cast(msg, state))
      {:sync, pid, ref, msg} ->
        loop(module, module.handle_call(msg, {pid, ref}, state))
    end
  end
end

子猫サーバの汎用化

子猫サーバの再実装です。kitty_server2 は、my_server に定義したインターフェースに対応するコールバックモジュールとして実装します。

defmodule KittyServer do
  defmodule Cat, do: defstruct name: "", color: :green, description: ""

  ### Client API
  def start_link, do: MyServer.start_link(__MODULE__, [])

  ## Synchronous call
  def order_cat(pid, name, color, description), do: MyServer.call(pid, {:order, name, color, description})

  ## This call is asynchronous
  def return_cat(pid, cat = %Cat{}), do: MyServer.cast(pid, {:return, cat})

  ## Synchronous call
  def close_shop(pid), do: MyServer.call(pid, :terminate)

  ### Server functions
  def init([]), do: [] ## no treatment of info here!

  def handle_call({:order, name, color, description}, from, cats) do
    case cats do
      [] ->
        MyServer.reply(from, make_cat(name, color, description))
        cats
      [h|t] ->
        MyServer.reply(from, h)
        t
    end
  end

  def handle_call(:terminate, from, cats) do
    MyServer.reply(from, :ok)
    terminate(cats)
  end

  def handle_cast({:return, cat = %Cat{}}, cats) do
    [cat|cats]
  end

  ### Private functions
  defp make_cat(name, color, description), do: %Cat{name: name, color: color, description: description}
  defp terminate(cats) do
    for %Cat{name: name} <- cats, do: IO.puts("#{name} was set free.")
    exit(:normal)
  end
end

14.2 基本的なサーバ で試したのと同じことをしてみる.

Eshell V8.1  (abort with ^G)
Interactive Elixir (1.3.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> import_file("my_server.ex")
{:module, MyServer,
 <<70, 79, 82, 49, 0, 0, 15, 168, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 1, 197,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:loop, 2}}
iex(2)> import_file("kitty_server2.ex")
{:module, KittyServer,
 <<70, 79, 82, 49, 0, 0, 14, 204, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 2, 73,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:terminate, 1}}
iex(3)> pid = KittyServer.start_link
#PID<0.96.0>
iex(4)> cat1 = KittyServer.order_cat(pid, :carl, :brown, "loves to burn bridges")
%KittyServer.Cat{color: :brown, description: "loves to burn bridges",
 name: :carl}
iex(5)> KittyServer.return_cat(pid, cat1)
:ok
iex(6)> KittyServer.order_cat(pid, :jimmy, :orange, "cuddly")
%KittyServer.Cat{color: :brown, description: "loves to burn bridges",
 name: :carl}
iex(7)> KittyServer.order_cat(pid, :jimmy, :orange, "cuddly")
%KittyServer.Cat{color: :orange, description: "cuddly", name: :jimmy}
iex(8)> KittyServer.return_cat(pid, cat1)
:ok
iex(9)> KittyServer.close_shop(pid)
carl was set free.
:ok
iex(10)> KittyServer.close_shop(pid)
** (UndefinedFunctionError) function :noproc.exception/1 is undefined (module :noproc is not available)
    :noproc.exception([])
    iex:14: MyServer.call/2

コードを共通化/抽象化したが同じ動きをしていることが確認できた.

このエントリーをはてなブックマークに追加