Elixir + Nuxt.jsでExDocを使いやすくしてみた

この記事は [Elixir Advent Calender 2018](https://qiita.com/advent-calendar/2018/elixir) 13日目の記事です

みなさんElixirでドキュメント書いていますか？
僕はサボってます（懺悔）

まぁその理由は追い追い書きますがElixirでドキュメントを書く時(読む時)に少し不満があったのでNuxt.jsをフロントエンド、Elixir + Cowboyをバックエンドに独断と偏見で[ExDoc](https://hexdocs.pm/elixir/writing-documentation.html)をいい感じに書き直したドキュメントビューワを[作ってみました](https://github.com/shufo/ex_doc_refined)。

## こんな感じ

![screencast20181212223342_2.gif](https://qiita-image-store.s3.amazonaws.com/0/47983/eb88d365-ea3e-711d-62b0-2229348ac3cb.gif)


## やったこと

[ExDocRefined](https://github.com/shufo/ex_doc_refined) という Elixir 標準の `ex_doc` 代替のドキュメントビューワを作りました。

たぶんElixirを使っている人ならex_docで出力したことはなくてもこういったドキュメントなら書いたことある人は多いんじゃないでしょうか。

```elixir
@doc """
This is a document for function
"""
```

この状態で`ex_doc` を依存に追加して `mix docs` コマンドを叩くと[Elixir公式リファレンス](https://hexdocs.pm/elixir/Kernel.html#content)のようなドキュメントが出力されます（Elixir公式ドキュメントもex_docを使って出力されています）

```bash
$ mix docs
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/47983/fb19cca2-436c-e18d-235c-3c8422d658d3.png)



### ExDocの問題点

![image.png](https://qiita-image-store.s3.amazonaws.com/0/47983/a532e71f-381b-ebc1-479e-6d823cb0c020.png)


複数人で開発するようなチーム開発の場合や1人で作る場合においても一年後の自分のためドキュメントを書くことは重要です。Elixirにおいてドキュメントを書くことは大事な文化の一つです。

>Elixir treats documentation as a first-class citizen (公式ドキュメントより)

ex_docはモジュールに埋め込んだドキュメントをHTMLに変換してくれて見やすくしてくれます。

しかし `ex_doc` を使っている時にまず問題意識としてあったのは

- 毎回`mix docs`を叩かないとドキュメントの変更がHTMLに反映されない (変更する度に想定したとおりに出力されているか確認するのがしんどい)
- ドキュメントがモジュール毎にHTMLで出力されるため遷移が発生してフローが途切れる
- 正直あまりUI/UXを意識した作りではないのでドキュメントを探すモチベーションが下がる
- 基本的にローカルでの出力のみなのでドキュメントをホスティングしてチームに共有などする場合には別途ホスティングする仕組みが必要(hexdocs.pmの[プライベートリポジトリ](https://hex.pm/pricing)を利用するなど)
- Summaryとfunction詳細の説明に同じ内容が出力されていて冗長

というところでした。

細かい点ですが開発において、こういった些細なことこそが重要で、開発体験を大きく左右します。開発が進めば進むほどプロジェクトにおける時間の殆どは既存の機能のメンテナンスや他人の書いたコードを読むことになるため、少しでもコードやドキュメントを読む苦労が減るのはメンテナビリティを大きく左右するからです。

そこで多少冗長性を廃して、Developer Experienceにも配慮しつつClosedなソースを対象にしたドキュメントビューワを作りたいと思いNuxtをフロントエンド、ElixirでCowboyをバックエンドにしたドキュメントビューワを作りました。

なぜClosedなソースをターゲットにしたかというとExDocの仕組みや冗長な書き方はElixir公式のようなPublicなコードを想定しているのでは？という推測をしたからです。Publicなソースに限定されないのなら色々別なことも出来るなと考えました。

### どうやって動作するか

![elixir_cowboy.png](https://qiita-image-store.s3.amazonaws.com/0/47983/a9ed2c53-e59e-c06a-6efd-230e6ee466a6.png)

簡単なアーキテクチャとしては以上のようになり、CowboyへはNuxtからWebsocketで接続しています。
Websocketで接続後、動的にコンパイル済みのBEAMファイルからドキュメントを取得してきます。
APIベースでないのは以前作った[LogViewer](https://github.com/shufo/log_viewer)というElixir向けのログビューワをベースに作ったのでその仕組みの再利用でしたが結果的にはSPAと相性がよくUXという点でいい結果になったかと思います。

## 出来ること

ExDocRefinedでは以下のことが出来ます

- インタラクティブなElixirプロジェクトのドキュメント表示
  - `mix docs`不要, 都度コンパイル済みのbeamファイルからドキュメントを参照
  - Markdown形式, ElixirコードのSyntax highlight対応
- Phoenixへの埋め込み (1.3, 1.4対応)
  - 開発サーバなどにドキュメントをホスティング可
- **ドキュメント上での対話的な関数の実行**
  - 以前からドキュメントを見る→`iex`で実際に試してみるという動作の間に少し無駄があるなと思っていました。なのでドキュメント上から直接パラメータを渡して実行し結果を見ることが出来るようにしました
  - 任意の型(Integer, String, Atom, Map, List, Struct, Module, Function, etc...)を渡してドキュメント上で関数を実行出来ます
    - 関数も渡せるので`String.to_atom("foo")`といった任意の関数callを渡しても大丈夫です（リモートサーバにデプロイする際は要認証機構）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/47983/48da5b69-080d-6403-5c03-e0d0381f0ed0.png)


実際に試してみたい方は[ExDocRefined](https://github.com/shufo/ex_doc_refined)のドキュメントを参考にインストールしてみてください

## 作って学んだこと

2個程Nuxtを使ってElixirの足回りのツールを作ってみて学んだことなど

- 単純な機能ならPhoenixを使わなくてもCowboyとPlugだけでも実装は可能
- 文字列でもElixirが解釈可能ならASTに変換出来、強力な操作が出来る
- Vue.jsのProgressiveにスケールするという発想とElixirの良い意味での型のゆるさが相性よくシームレスに作業出来る


## 今後の予定

今後やりたいことなど

- ドキュメント取得箇所を自前で書いたが、ex_docの関数を使って取得するようにし公式の仕様変更に追従出来るようにする
- 細かいUIの改善（モジュールの絞り込み, specの表示方法等）
- オプションでドキュメント上での関数実行を切り替え出来るように

## まとめ

ex_docをいい感じにしたいということで[ExDocRefined](https://github.com/shufo/ex_doc_refined)というドキュメントビューワを作りました
最初はex_docのformatter behaviourに対応したサードパーティのformatterを作りSPAを出力しようかと思ったけど、結局`mix docs`コマンドを毎回叩かなきゃいけないことやformatterの内部実装に影響されそうということで完全にサードパーティとして作ることにしました

Elixirは最近はライブラリに困るということも少なくなってきたと思いますが、こういった足回りのツールがまだ少ないと思うところもあるので、これで少しでも何らかの貢献になれればありがたいです