Elixir

Elixir1.6のformatterの挙動を観察してみた

Elixir1.6のformatterを使っているという記事を見かけたので羨ましくなって試してみた。

サイボウズLiveを作るで書いたコードを一括でformatterにかけてみた。今回対象にしたのはex, exsのみ。

リストやマップが綺麗に

自動生成されたデフォルトのmix.exs等のコードは変なリストの書き方だったので改善された。自動生成されたモデルのひどいcastも全部修正される。ちなみにリストやマップの最後にカンマがある場合は削除される。

before
    [mod: {Cybozulive.Application, []},
     applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext,
                    :phoenix_ecto, :mariaex, :ueberauth_twitter, :arc_ecto, :timex]]
after
    [
      mod: {Cybozulive.Application, []},
      applications: [
        :phoenix,
        :phoenix_pubsub,
        :phoenix_html,
        :cowboy,
        :logger,
        :gettext,
        :phoenix_ecto,
        :mariaex,
        :ueberauth_twitter,
        :arc_ecto,
        :timex
      ]
    ]

逆に1行で収まる場合は1行にしてくれる。

before
  pubsub: [name: CybozuliveWeb.PubSub,
           adapter: Phoenix.PubSub.PG2]
after
  pubsub: [name: CybozuliveWeb.PubSub, adapter: Phoenix.PubSub.PG2]

スペースの桁揃え削除

誰かのこだわりのスペース揃えが削除された。

before
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]
after
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

Ecto

通常のコードでは括弧をつけるのが一般的だが、schemaだけ無しで自動生成されてたみたいなので一通り追加されていた。routerも同様。

before
    field :body, :string
after
    field(:body, :string)

あとqueryも括弧有りに修正されている。Ectoのマニュアルも変わっていくのかな。

before
    query = from g in Group,
      where: g.id == ^id and is_nil(g.deleted_at)
after
    query = from(g in Group, where: g.id == ^id and is_nil(g.deleted_at))

長いやつ。これはどちらかというと分割していった方が綺麗かもしれないので、自分でちゃんと整えたほうがよいかもしれない。

before
    query = from i in Invitation,
      where: i.user_id == ^user.id and i.group_id == ^group.id and is_nil(i.closed_at) and is_nil(i.deleted_at)
before
    query =
      from(
        i in Invitation,
        where:
          i.user_id == ^user.id and i.group_id == ^group.id and is_nil(i.closed_at) and
            is_nil(i.deleted_at)
      )

長いif文

演算子は行末。なぜ2行目がスペース5つなのかはよくわからない。

before
    if (is_nil(board_topic.group.deleted_at)
      && Enum.any?(conn.assigns.joining_groups, fn(group) -> group.id == board_topic.group_id end)) do
after
    if is_nil(board_topic.group.deleted_at) &&
         Enum.any?(conn.assigns.joining_groups, fn group -> group.id == board_topic.group_id end) do

長いif & else

無理やり1文で書いて長くなったif & else

before
    if schedule.schedule_category, do: schedule.schedule_category.bg_color, else: schedule.bg_color
after
    if schedule.schedule_category,
      do: schedule.schedule_category.bg_color,
      else: schedule.bg_color

これも1文で書けないなら普通に下記の方が良さそう。

    if schedule.schedule_category do
      schedule.schedule_category.bg_color
    else
      schedule.bg_color
    end

空行が入る

合間で空行を入れて見やすくしたいらしい。condとかも。

    case Repo.update(changeset) do
      {:ok, board_post} ->
        conn
        |> put_flash(:info, "Board post updated successfully.")
        |> redirect(to: board_post_path(conn, :show, nil, board_post))

      {:error, changeset} ->
        render(conn, "edit.html", board_post: board_post, changeset: changeset)
    end

とか

    date_text = Timex.format!(date, "{YYYY}-{0M}-{0D}")

    if time do

逆に連続した空行は1行まで削られる。

無理やり1行にした条件はだめ

before
      nil -> attrs
after
      nil ->
        attrs

長いrenderが複数行に

これは自分でちゃんとassignでパイプした方がいい。

before
    render(conn, "index.html", board_posts: board_posts, board_topic: board_topic, changeset: changeset)
after
    render(
      conn,
      "index.html",
      board_posts: board_posts,
      board_topic: board_topic,
      changeset: changeset
    )

パイプラインの戻り値

変数に入れる時は代入文を1行にして後はインデント

before
    board_topics = Repo.all(query)
    |> Repo.preload(:user)
    |> Repo.preload(:board_category)
after
    board_topics =
      Repo.all(query)
      |> Repo.preload(:user)
      |> Repo.preload(:board_category)

パイプラインでなくても同様

before
    result = Repo.transaction(fn ->
      Groups.update_invitation!(invitation, invitation_params)
      Groups.create_group_user!(invitation.user, group)
    end)
after
    result =
      Repo.transaction(fn ->
        Groups.update_invitation!(invitation, invitation_params)
        Groups.create_group_user!(invitation.user, group)
      end)

括弧なしの関数呼び出しはだめ

いくつも修正されてるからどうもだめっぽい。まあたしかにRailsでもそうだけど関数なのかどうかぱっとみよく分からん。

before
    invitation_params = %{"deleted_at" => Timex.now}
after
    invitation_params = %{"deleted_at" => Timex.now()}

まとめ

  • Elixir & Phoenixの場合、いわゆるコーディング規約みたいなのがどこを参考にすればよいかよくわからないところがあったので、非常に助かる。
  • useとかaliasの順番が決まっているとかどこかで見たような気がするが、そういうのは特に変更されないっぽい。
  • 1行何桁までOKなのか試したら、104桁までOKだった。どういう基準だろ。ちなみにdef文1パターンしか調べてないので他は不明。
  • 個人的には勝手に1行にしてくれるのが好き。