Elmの「あの感じ」のドキュメントを社内スコープで展開してみます

Elm Advent Calendar2017、今年は去年よりも大いに盛り上がっていて、沢山の人達が沢山の情報を上げていて、毎日楽しく拝見しております。
それでは最終日、行かせて頂きます。

# 社内向けドキュメントの展開

ワタクシの務めるFringe81ではElmを用いたプロダクトが現在２つ稼働しています。
３つめ４つめも仕込んでいくような気配もあるので、徐々に社内共通で使うライブラリも作り出したりするようになってきました。
さて、このライブラリ、仮に[Elm Packagesで一般向けに公開](http://package.elm-lang.org/)しようとなるとあらゆる人に使いやすく設計する必要があるため、グッと敷居が高くなるんですが、プロダクト開発速度を考えると結構な労力を使って一般化レベルに持っていくまでの余裕は無かったり。
という事で、社内最適化を考えたときに、社内スコープでドキュメントを参照してもらいたいという欲求に対する１つのパターンを提示できればなーと思います。
さらにドキュメントはElm Packagesの体裁で一貫性を持たせていこうと思います(カッコイイですよね♪)。

<img width="400" alt="スクリーンショット 2017-12-24 16.53.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/39330/1278b0c8-f58a-9bf0-91d9-070f1006d170.png">

# 全体の流れ

ステップとしては下記のような感じです

```
ソースコードにコメント書く 
  -> ソースコメントからドキュメントファイルを生成 
  -> テスト表示 
  -> ドキュメントサイト構築 
  -> 公開/展開
```

# ドキュメントの記述

ドキュメンテーションフォーマットに関しては[公式ガイド](http://package.elm-lang.org/help/documentation-format)があります。まずテキトーに作ったアプリケーションをもとに、このガイドに沿ってコメントを付けていきたいと思います。

モジュールが複数あったほうが応用が聞くと思うので、Main.elmと、モデルの知識をカプセル化したTypes/Model.elmの２モジュールを作ってみます。

```elm:Main.elm
module Main exposing (Msg, init, main, update, view)

{-| This application offers transforming a model into gained or losing.

# Definition
@docs Msg

# Determine a next model.
@docs init, update

# Create view
@docs view

# Entry point
@docs main
-}

import Html exposing (Html, button, div, span, text)
import Html.Events exposing (onClick)
import Types.Model as Model exposing (Model)


{-| All of action of this Application.
-}
type Msg
    = Gain
    | Lose


{-| Initial model and effects.
-}
init : ( Model, Cmd Msg )
init =
    ( Model.initial, Cmd.none )


{-| Updating model and generating effects.
-}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Gain ->
            ( Model.gain model, Cmd.none )

        Lose ->
            ( Model.lose model, Cmd.none )


{-| Create views of this application.
-}
view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Lose ] [ text "lose" ]
        , span [] [ text <| toString model ]
        , button [ onClick Gain ] [ text "gain" ]
        ]


{-| The entry point of this application.
-}
main : Program Never Model Msg
main =
    Html.program
        { view = view
        , init = init
        , update = update
        , subscriptions = always Sub.none
        }
```

```elm:Types/Model.elm
module Types.Model exposing (Model, gain, initial, lose)

{-| This module have a knowledge of model.
For example, a initial state and a way of how to change it.

# Definition
@docs Model

# Initial Model
@docs initial

# Changing Model
@docs gain, lose
-}


{-| Type of model.
-}
type alias Model =
    Int


{-| Initial model.
-}
initial : Model
initial =
    0


{-| Change a model to gained form.
-}
gain : Model -> Model
gain m =
    m + 1


{-| Change a model to lost form.
-}
lose : Model -> Model
lose m =
    m - 1
```

# ドキュメントファイル生成＋簡易閲覧

Elm Packagesのドキュメントは専用のJSONフォーマットを読み込んで生成される模様。
準備として`elm-package.json`の`summary`にプロジェクト概要、`exposed-modules`に下記のようにドキュメント化したいモジュールを記述します。

```json
{
  ...
  "summary": "Sample Application",
  ...
  "exposed-modules": [ "Main", "Types.Model" ],
  ...
}
```

そしてコンソールを開いたらプロジェクトルート上で下記コマンドを流します。

```
elm-make --docs=documentation.json
```

これで`documentation.json`が生成されたと思います。
このファイルを[公式のドキュメントプレビューガイド](http://package.elm-lang.org/help/docs-preview)で開いてみましょう。

<img width="400" alt="スクリーンショット 2017-12-24 15.53.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/39330/9a55a865-e535-766a-4629-827497a343b1.png">

でました！おなじみの「あの感じ」で。
一応、会社内のElmer達に向けてこのページを開いてJSONファイル食わせてくれー、とお願いする運用も可能ではあるんですが、リロードすると消えちゃいますし、使い勝手が悪い！ということでもう少し運用し易い形に持っていきたいと思います。

# elm-docでドキュメントサイトを構築
[elm-doc](https://github.com/ento/elm-doc)さんを使うことで、一時的ではなくドキュメントサイトのブツを残すことが出来ます。なんて素晴らしいライブラリ！
利用者から見た簡易構築手順もサラッと残しておきます。

### Python環境の構築

elm-docはPythonを利用するようなのでまずは下記を準備します。ワタクシはpyenvを使って3.4.3にしました。

- pythonのバージョンを3.4以上に上げる
- [python-magic](https://github.com/ahupp/python-magic)をインストール

### elm-docのインストール

```
pip install --upgrade pip setuptools
pip install elm-doc
```

### elm-docでドキュメントサイトをビルド

[elm-docのREADME.md](https://github.com/ento/elm-doc/blob/master/README.md)を読めば、ファイル除外など色々とオプションがありますが、今回はスタンダードに。
プロジェクトルートで下記を実行します。

```
elm-doc . --output docs
```

これで`docs`というディレクトリ、配下にファイル群が生成されると思います。

### 閲覧環境を構築

この`docs`ディレクトリを社内サーバーで公開すれば完了でございます。
ただ、社内ライブラリということで、社内のElmersがカスタマイズしてプルリクしてくれるというシナリオも想定して、ローカルでの最新のドキュメントを参照しやすい方法も構築します。
今回は、[opener](https://www.npmjs.com/package/opener)と[node-web-server](https://www.npmjs.com/package/node-web-server)を使います。
まずはインストール。

```
npm i -D opener node-web-server
```

次に、プロジェクトルートにサーバーの設定ファイルを生成します。

```json:server.conf
{
  "host"       : "localhost",
  "port"       : 4000,
  "docRoot"    : "docs",
  "allowUnknownMIMEType": true,
  "MIME" : {
    "": "text/html"
  }
}
```

package.jsonにスクリプトを設定します。ポートは4000としてみます。

```json:
{
  ...
  "scripts": {
    "doc": "./node_modules/.bin/opener http://localhost:4000 && ./node_modules/.bin/nws -c server.conf"
  },
  ...
}
```

これでブラウザが開いてドキュメントサイトを表示されます。

```
npm run doc
```

### ドキュメントを見てみる

ではブラウザ上で、「あの感じ」で生成されたファイルを閲覧していきたいと思います。

#### サイトのルート

`docs/index.html`はプロジェクトのドキュメントリンクと共に、依存パッケージのドキュメントも包含してくれます。
今回、user/projectという名前になっているのは`elm-package.json`の`repository`のURLの記述のためです。
本来、githubのURLを入れるんですが、ウチはgitlabを使っているので、そのままにしています。

<img width="800" alt="スクリーンショット 2017-12-24 16.23.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/39330/6a40ee0e-5593-020d-4a9e-5f69ddb1dcd2.png">

#### プロジェクトのルート

プロジェクト直下のREADME.mdの内容が表示されます。
右側のリンクでモジュールのドキュメントにアクセス出来ます。

<img width="800" alt="スクリーンショット 2017-12-24 16.51.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/39330/3085a503-82d0-003e-8566-73a920aa0a81.png">

#### モジュールページ

見慣れたページの感じになっています。これで社内Elmer達がお仕事をしやすくなってくれると嬉しい！

<img width="800" alt="スクリーンショット 2017-12-24 16.53.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/39330/1278b0c8-f58a-9bf0-91d9-070f1006d170.png">

今回は以上でございます。
この仕組みが使いやすいのか否かはまだ検証フェーズで御座います(笑)。
それでは皆様、良いお年を！