すごいE本 第17章 後半戦 on Elixir (DynamicSupervisor)

# 環境

```shell-session:sh
$ lsb_release -d
Description:    Ubuntu 18.04.2 LTS

$ elixir -v
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.8.1 (compiled with Erlang/OTP 20)
```

# 17.3 バンドで練習
## simple_one_for_one スーパバイザを使う

`:simple_one_for_one` のとき、子の仕様（全て一律）は辞書で保持されるらしい。
それ以外の戦略の場合はリストで保持されるので子が多い場合は前者がいいんだね。

`Supervisor.start_child(sup, [arg, ...])` とすると、
`:simple_one_for_one` の `sup` の子仕様 `start: {mod, :start_link, args}` を探し、
`apply(mod, :start_link, args++[arg, ...])` を呼び出す。
ややこいね。

Elixir では非推奨だけどやってみよう。

```elixir:band/lib/band.ex
defmodule Band do
  use Supervisor
  alias __MODULE__, as: Me
  alias __MODULE__.Musician
  ...
  # 追加
  @impl true
  def init(:jamband) do
    child = %{
      # 親が子を識別するための child_id
      id: :jam_musician,
      start: {Musician, :start_link, []},
      restart: :temporary,
    }

    Supervisor.init([child], strategy: :simple_one_for_one)
  end
  ...
end
```
```elixir:iex
iex(1)> Band.start_link(:jamband)
{:ok, #PID<0.147.0>}
iex(2)> Supervisor.start_child(Band, [:djembe, :good])
Dorothy Frizzle(good djembe) は部屋に入った。
{:ok, #PID<0.149.0>}
Dorothy Frizzle(good djembe) の音はいいね！
...
iex(3)> Supervisor.start_child(Band, [:djembe, :good])
{:error, {:already_started, #PID<0.149.0>}}
...
```

`Supervisor.start_chid(Band, [:djembe, :good])` が、
`apply(Band.Musician, :start_link, []++[:djembe, :good])` を呼び出す。

`Musician` の定義を思い出すと、この `:djembe` で名前を登録するのであった。
なわけで、２回目の `start_child/2` で名前衝突が起きている。
こういう状況に便利な `Registry` というものがあるが、
話が長くなるので、ここでは E本の流れに準じ単に違うアトムを渡す。

```elixir:iex
...
iex(4)> Supervisor.start_child(Band, [:drum, :good])
Wanda Perlstein(good drum) は部屋に入った。
{:ok, #PID<0.152.0>}
...
iex(5)> Supervisor.start_child(Band, [:guitar, :good])
Phoebe Li(good guitar) は部屋に入った。
{:ok, #PID<0.154.0>}
...
iex(6)> Supervisor.terminate_child(Band, :djembe)
{:error, :simple_one_for_one} # あれれ？
...
iex(7)> Band.Musician.stop(:djembe)
Dorothy Frizzle(good djembe) は部屋を出た。
:ok
...
```

### うまい話ばかりじゃないですよ

うーん、話が食い違うねー。
Erlang 20 でコンパイルされた Elixir なんだけど、どーして？

```elixir:iex
...
iex(6)> Supervisor.terminate_child(Band, Process.whereis(:djembe))
マネージャーは怒りバンドを解散させた！
Dorothy Frizzle(good djembe) は地下鉄へ去っていった。

:ok
```

誤植に耐えてよく頑張った！感動した！

## ※ `DynamicSupervisor` を使ってみる

[DynamicSupervisor — Elixir vx.x.x](https://hexdocs.pm/elixir/DynamicSupervisor.html#content)

`Supervisor` から `:simple_one_for_one` を排除したいのね。
別のモジュールにしちゃった方が見通しが良いでしょということかな。

```shell-session:sh
$ touch lib/band/jam.ex
```
```elixir:band/lib/band/jam.ex
defmodule Band.Jam do
  use DynamicSupervisor
  alias __MODULE__, as: Me
  alias Band.Musician

  ### use で child_spec/1 が自動的に定義される。 ###
  # def child_spec(arg) do
  #   %{
  #     id: __MODULE__,
  #     start: {__MODULE__, :start_link, [arg]},
  #     type: supervisor
  #   }
  # end

  def start_link do
    DynamicSupervisor.start_link(Me, [], name: Me)
  end

  def stop, do: DynamicSupervisor.stop(Me)

  def start_child(role, skill) do
    child = %{
      id: Musician,
      start: {Musician, :start_link, [role, skill]},
      restart: :temporary
    }

    DynamicSupervisor.start_child(Me, child)
  end

  def terminate_child(role) do
    pid = Process.whereis(role)
    DynamicSupervisor.terminate_child(Me, pid)
  end

  @impl true
  def init([]) do
    # 子仕様リストとして返却しなくて済むようになった。
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end
```
```elixir:iex
iex(1)> Band.Jam.start_link()
{:ok, #PID<0.166.0>}
iex(2)> Band.Jam.start_child(:djembe, :good)
Carlos Frizzle(good djembe) は部屋に入った。
{:ok, #PID<0.168.0>}
Carlos Frizzle(good djembe) の音はいいね！
...
iex(3)> Band.Jam.start_child(:drum, :good)
Dorothy Li(good drum) は部屋に入った。
{:ok, #PID<0.170.0>}
...
iex(4)> Band.Jam.start_child(:guitar, :good)
Wanda Terese(good guitar) は部屋に入った。
{:ok, #PID<0.172.0>}
...
iex(5)> Band.Jam.terminate_child(:djembe)
マネージャーは怒りバンドを解散させた！
Carlos Frizzle(good djembe) は地下鉄へ去っていった。

:ok
...
iex(6)> Band.Jam.stop()
マネージャーは怒りバンドを解散させた！
Dorothy Li(good drum) は地下鉄へ去っていった。

マネージャーは怒りバンドを解散させた！
Wanda Terese(good guitar) は地下鉄へ去っていった。

:ok
```

うむ、言語設計者の哲学を感じるね。

動的に子を追加していく場合、今回の様にタプルで名前をつけていくのはよろしくない。
タプルはガベージコレクションの対象ではないからだ。
自前で対策を踏むよりも `Registry` を利用した方が良さそうだ。
だが、この章の段階では手を出すには早すぎるだろう。

次章からはアプリケーションだ。