Elixirで最近覚えた書き方

最近仕事でElixir使うようになって覚えた書き方をメモしておきます(version1.5.0)。
（もっと分かってくれば他にも書いていきます）

## Elixir本体
### 構造体のパターンマッチ

```elixir
def func(%SomeStruct{status: some_status}, arg2, arg3) do
  # 何らかの処理
end
```

関数のヘッド部で構造体のフィールドを変数に束縛させてそれを関数の内部で利用するときに使ってます。
ガードと組み合わせて処理をフィールドの値によって振り分けるときにも使えます。

```elixir
def func(%SomeStruct{status: some_status}, arg2, arg3) when some_status in [0, 1, 2] do
  # 何らかの処理
end
def func(%SomeStruct{status: some_status}, _, _) do
  raise("invalid status: #{some_status}")
end
```

引数の型でパターンマッチもできる（spec書けば不要です）

```elixir
def func(%SomeStruct{}=arg) do
  # 何らかの処理
end
def func(%OtherStruct{}=arg) do
  # 何らかの処理
end
```

### Caseとパイプ
使いすぎると可読性が下がりますが関数をパイプでCase式に繋げて処理分岐します。
(例が悪くて申し訳ないです)

```elixir
# arg = [0, 1, 2, 3, 4]
def func(arg) do
  |> Enum.filter(arg, &Integer.is_odd())
  |> Enum.count
  |> case　do
    2 -> "何らかの処理"
    _ -> "何らかの処理"
  end
end
```
当然ifでも出来ます（が使う機会は今のところありません）

### `Module.Concat`
namespaceをつけてモジュールを宣言するときに使います。モジュールを動的に生成するときに使うことが多いです。

```elixir
contents  = quote do
  def func(), do: "Hello Elixir!"
end

Module.create(Module.concat(Elixir, Hello), contents, Macro.env.location(__ENV__))

Hello.func()
"Hello Elixir"
```

### 構造体の更新
以下の書き方でフィールドを更新した構造体を再作成出来ます。

```elixir
defmodule Hello
  defstruct [:a, :b, :c, :d]
end

val1 = %Hello{a: 1, b: 2}
# フィールドを更新する
val2 = %Hello{val1|a:3, b: 4} 
```

Mapの場合は元から用意されている`update/3`か`update/4`を使うことも多いです

```elixir
val = %{a: 1, b: 2}
val2 = Map.update(val, :a, &(&1+1)) 
#=> %{a: 2, b: 3}
val3 = Map.update(val2, :c, 4, &(&1+1))
#=> %{a: 2, b: 3, c:4}
```

### `IO.inspect`のラベル
デバッグのときに便利です

```elixir
arg
|> IO.inspect(label: "before func")
|> func()
|> IO.inspect(label: "after func")
```

### 文字列分割
文字列操作で一字ずつ分割したい場合。
（あまり文字列操作する機会ないですが）

```elixir
String.split("abcde", "", trim: true)
["a", "b", "c", "d", "e"] 
```

### 時刻偽装
Rubyのtimecopみたいなものを使いたいなと考えていたのですが、[ここ](http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/)でJoseさんがMockについて語っているのをみ、てElixirではあまりMockを推奨してなさそうなのでテストの時に時刻ライブラリを差し替える方法を使っています。

```elixir
defmodule AppName.Support.TimeMock do
  use Agent
  use Timex

  def start_link do
    Agent.start_link(fn -> %{is_m: false, m_val: nil} end, name: __MODULE__)
  end

  def now() do
    state = Agent.get(__MODULE__, fn(state) -> state end)
    if state[:is_m] do
      state[:m_val] |> Timex.from_unix(:microsecond)
    else
      :os.system_time(:microsecond) |> Timex.from_unix(:microsecond)
    end
  end

  def freeze do
    freeze(:os.system_time(:microsecond))
  end

  def freeze(timestamp) do
    Agent.update(__MODULE__, fn(_state) -> %{is_m: true, m_val: timestamp} end)
  end

  def unfreeze do
    Agent.update(__MODULE__, fn(_state) -> %{is_m: false, m_val: nil} end)
  end
end
```
このようなモジュールをテスト時のみ使用するライブラリとして用意しておいて、
下記のようにテスト実行時にライブラリを差し替えるようにしています。テスト用のconfigファイルに上で作成したモジュールを指定する必要があります。

```elixir
@time_module Application.get_env(:app_name, :time)[:time_module] || Timex

def func() do
  epoch = @time_module.now |> Timex.to_unix
  # 何らかの処理
end
```

テスト時には以下のようにして時刻偽装をしています。

```elixir
describe "get, set" do
  let :user, do: Hello.Fixtures.User.create
  let :basetime, do: TimeMock.now()

  before_all do
    TimeMock.freeze() # 時刻を止める
  end
  after_all do
    TimeMock.unfreeze() # 時刻を元に戻す
  end

  context "time change" do
    it do
      # 1秒前に進める
      Timex.shift(basetime(), seconds: 1) 
      |> DateTime.to_unix(:microsecond) 
      |> TimeMock.freeze      

      # 何らかのテスト...
    end
  end
end
```


## ライブラリ
### Mix.taskのaliases
複数のタスクをまとめられます。

```elixir
defp aliases do
  ["ecto.setup": ["ecto.create", "ecto.migrate"]]
end
```


### Ectoで発行するクエリの確認
どんなSQLになるのか確認したい場合に使っています。

```elixir
query = from(u in User, where: u.user_id == ^user_id, update: [inc: ref_cnt: 10])
Ecto.Adapters.SQL.to_sql(:update_all, repo, query)
# {"UPDATE `users` AS u0 SET u0.`ref_cnt` = u0.`ref_cnt` + 10 WHERE (u0.`user_id` = ?)", [100, 10]}
```


## 感想
Elixirはもともと文法がシンプルでコードが読みやすいところが良いと思ってました。
今回仕事で初めて使うことになって長く触ってるうちに、文法はシンプルながらも便利な記法が用意されていて、かつライブラリもまぁまぁ困らないくらいにはあるので非常に実用的な言語だと感じてます。