elixirのGenServerで色々実験してみた。

# 基本のソース

```ex
defmodule NumStore do
  use Application
  def start(_type, _args) do
    import Supervisor.Spec, warn: false
    children = [
      worker(NumStore.Worker, []),
    ]
    opts = [strategy: :one_for_one, name: NumStore.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

defmodule NumStore.Worker do
  use GenServer
  # Callbacks
  def start_link() do
    GenServer.start_link(__MODULE__, 0, name: __MODULE__)
  end
  def init (state) do
    :global.register_name :server, self() # <= 適当に名前を登録
    {:ok, state}
  end
  def handle_cast(:succ, state) do
    {:noreply, state+1}
  end
  def handle_call(:current, _from, state) do
    {:reply, state, state}
  end
end

```

# サーバー側起動
succで状態の内容を+1。currentで値を取得する。

```bash
# elixirコマンドの場合は--no-halt。
$ iex  --name server@127.0.0.1 -S mix
iex(server@127.0.0.1)1> pid = :global.whereis_name(:server)
#PID<0.XXXX.0>
iex(server@127.0.0.1)2> :gen_server.cast(pid, :succ)
:ok
iex(server@127.0.0.1)3> :gen_server.call(pid, :current)
1
```

# 別nodeから接続

```
$ iex  --name client@127.0.0.1
iex(client@127.0.0.1)1> Node.connect(:"server@127.0.0.1")
true
iex(client@127.0.0.1)2> :global.sync() <= elixirコマンドでやる場合は必要。
:ok
iex(client@127.0.0.1)3> Node.list
[:"server@127.0.0.1"]
```

# 別nodeからメッセージを送信

[公式ドキュメント](http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html)によると、
サーバーのpidか名前とnodeがあればいいらしい。

```
iex(client@127.0.0.1)4> pid = :global.whereis_name(:server)
#PID<XXXX.XX.0>
iex(client@127.0.0.1)5> :gen_server.call(pid, :current)
1
iex(client@127.0.0.1)6> :gen_server.cast(pid, :succ)
:ok
iex(client@127.0.0.1)7> :gen_server.call(pid, :current)
2
iex(client@127.0.0.1)8> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.cast(:succ)
:ok
iex(client@127.0.0.1)9> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.call(:current)
3
```

# 別nodeから変更

Module.eval_quotedで頑張ってみたけど、出来なかった。  
再定義以外にないものか・・・。

```
iex(client@127.0.0.1)10> Node.spawn :"server@127.0.0.1", fn ->
...(client@127.0.0.1)10>   defmodule NumStore.Worker do
...(client@127.0.0.1)10>     use GenServer
...(client@127.0.0.1)10>     def start_link() do
...(client@127.0.0.1)10>       GenServer.start_link(__MODULE__, 0, name: __MODULE__)
...(client@127.0.0.1)10>     end
...(client@127.0.0.1)10>     def init (state) do
...(client@127.0.0.1)10>       :global.register_name :server, self()
...(client@127.0.0.1)10>       {:ok, state}
...(client@127.0.0.1)10>     end
...(client@127.0.0.1)10>     def handle_cast(:minus, state) do # <= 追加
...(client@127.0.0.1)10>       {:noreply, state-1}
...(client@127.0.0.1)10>     end
...(client@127.0.0.1)10>     def handle_cast(:succ, state) do
...(client@127.0.0.1)10>       {:noreply, state+1}
...(client@127.0.0.1)10>     end
...(client@127.0.0.1)10>     def handle_call(:current, _from, state) do
...(client@127.0.0.1)10>       {:reply, state, state}
...(client@127.0.0.1)10>     end
...(client@127.0.0.1)10>   end
...(client@127.0.0.1)10> end
iex(client@127.0.0.1)11> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.cast(:minus)
:ok
iex(client@127.0.0.1)12> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.call(:current)
2
```

# まとめ

サーバー（NumStore.Worker）の定義を上書きしても状態保持しているのが謎技術。