Google Spread Sheetで複数列を取得してElmのモデルに入れたメモ

## 概要

[Google Sheet APIが使えるようになった][*1]。
[ElmでSheet APIを使えるようになった][*2]。
もう少し大きなシートを使ってページを更新できるようにしてみる。

出来上がったのがこちら。google spread sheetでカードの文言などを編集できる。

[ユーザカードページ](https://garden-2a6de.firebaseapp.com/sandbox/sample)
※ 現在、ソースの状態とは実装がずれております。完成品の大まかなイメージは変わっていません。

[この時点のソース](https://github.com/hibohiboo/garden/tree/e5d7a58942cbd5895426d893794dca5e1e3d93e4)  

デコーダの書き方が課題点となっている。

* ～でなければのときの書き方が冗長になっている。
  * [アドバイスいただきました！](https://qiita.com/hibohiboo/items/e1c4b3e47a3fb348d35c)
* 現在の書き方だと、スプレッドシートとの相性が悪い？ 列を追加したときに、そこから先の番号を手作業で１つずつ更新が必要。

## 開発環境
* Windows 10
* Vagrant 2.2.4
* Virtualbox 6.0.6r130049
* Ubuntu 18.04.2 LTS (Bionic Beaver)
* Docker version 18.09.5, build e8ff056
* docker-compose version 1.24.0, build 0aa59064

## ソース

### モデル定義

```elm
type alias CardData =
    { cardId : String
    , cardName : String
    , cardType : String
    , kind : String
    , exp : Int
    , timing : String
    , cost : Int
    , range : Int
    , maxRange : Int
    , target : String
    , maxLevel : Int
    , effect : String
    , description : String
    , tags : List Tag
    , imgMain : String
    , illustedByName : String
    , illustedByUrl : String
    , imgFrame : String
    , frameByName : String
    , frameByUrl : String
    , deleteFlag : Int
    }
type alias Tag =
    { name : String
    , level : Int
    }


initCard =
    CardData "" "" "" "" 0 "" 0 0 0 "" 0 "" "" [] "" "" "" "" "" "" 0
```

タグを複数持ったカード。タグについてはスプレッドシートでは1つのセルに"タグ,タグ:Lv,タグ..."という形で記述する。
:Lvがないときは0として扱う。

### タグのデコード

```elm
tagsDecoder : Decoder (List Tag)
tagsDecoder =
    D.map tagsParser string


tagsParser : String -> List Tag
tagsParser s =
    let
        list =
            String.split "," s
    in
    List.map (\str -> tagParser str) list


tagDecoder : Decoder Tag
tagDecoder =
    D.map tagParser string


tagParser : String -> Tag
tagParser s =
    let
        list =
            String.split ":" s

        name =
            case List.head list of
                Just a ->
                    a

                _ ->
                    s

        value =
            case List.tail list of
                Just t ->
                    case List.head t of
                        Just a ->
                            case String.toInt a of
                                Just n ->
                                    n

                                _ ->
                                    0

                        _ ->
                            0

                _ ->
                    0
    in
    Tag name value
```

「:Lvがないときは0として扱う。」の場合分けがかなりの力技になっている。
何か、もっといいやり方がありそうだけれど、今後の課題。

### カードのデコード

```elm
import Json.Decode as D exposing (..)
import Json.Decode.Pipeline exposing (custom, hardcoded, optional, required)
import Json.Encode as E

-- 省略

cardDataListDecodeFromJson : String -> Result Error (List CardData)
cardDataListDecodeFromJson s =
    decodeString (field "values" (D.list cardDecoder)) s


cardDecodeFromString : String -> Result Error CardData
cardDecodeFromString s =
    decodeString cardDecoder s


cardDecoder : Decoder CardData
cardDecoder =
    D.succeed CardData
        |> Json.Decode.Pipeline.custom (D.index 0 D.string)
        |> Json.Decode.Pipeline.custom (D.index 1 D.string)
        |> Json.Decode.Pipeline.custom (D.index 2 D.string)
        |> Json.Decode.Pipeline.custom (D.index 3 D.string)
        |> Json.Decode.Pipeline.custom (D.index 4 GSAPI.decoderIntFromString)
        |> Json.Decode.Pipeline.custom (D.index 5 D.string)
        |> Json.Decode.Pipeline.custom (D.index 6 GSAPI.decoderIntFromString)
        |> Json.Decode.Pipeline.custom (D.index 7 GSAPI.decoderIntFromString)
        |> Json.Decode.Pipeline.custom (D.index 8 GSAPI.decoderIntFromString)
        |> Json.Decode.Pipeline.custom (D.index 9 D.string)
        |> Json.Decode.Pipeline.custom (D.index 10 GSAPI.decoderIntFromString)
        |> Json.Decode.Pipeline.custom (D.index 11 D.string)
        |> Json.Decode.Pipeline.custom (D.index 12 D.string)
        |> Json.Decode.Pipeline.custom (D.index 13 tagsDecoder)
        |> Json.Decode.Pipeline.custom (D.index 14 D.string)
        |> Json.Decode.Pipeline.custom (D.index 15 D.string)
        |> Json.Decode.Pipeline.custom (D.index 16 D.string)
        |> Json.Decode.Pipeline.custom (D.index 17 D.string)
        |> Json.Decode.Pipeline.custom (D.index 18 D.string)
        |> Json.Decode.Pipeline.custom (D.index 19 D.string)
        |> Json.Decode.Pipeline.custom (D.index 20 GSAPI.decoderIntFromString)
```

作り方が悪い気がする。

* 列を追加したときに、そこから先の番号を手作業で１つずつ更新が必要。
* 今、どのプロパティを取得しているのかがぱっと見でわからない。

### json

今回解析した、GooogleAPIが返してくるjsonは以下のようなもの。
数値も文字列として返してくるのが注意かも。

```json
{
  "range": "cardList!A2:U3",
  "majorDimension": "ROWS",
  "values": [
    [
      "B000",
      "走る",
      "能力",
      "基本能力",
      "0",
      "アクション",
      "4",
      "0",
      "0",
      "自身",
      "1",
      "移動1",
      "逃げてもいいし、向かってもいい。\n君たちは何処にだっていける。\n一歩ずつではあるけれど。",
      "移動,基本能力:0",
      "/assets/images/card/main/run.png",
      "ヒューマンピクトグラム2.0",
      "http://pictogram2.com/",
      "/assets/images/card/frame/report.gif",
      "",
      "",
      "0"
    ],
    [
      "C000",
      "未定",
      "能力",
      "てすと",
      "",
      "",
      "0",
      "0",
      "0",
      "自身",
      "1",
      "",
      "",
      "",
      "",
      "",
      "",
      "",
      "",
      "",
      "0"
    ]
  ]
}
```

## 参考

[Google Sheet APIを試したメモ][*1]
[ElmでGoogle Spread Sheetの値を読み込んだメモ][*2]
[Elmの歩き方（入門者向けブックマーク集）][*3]
[Decoder a からいろいろ理解ってしまおう][*4]
[elm json pipline][*5]

[*1]:https://qiita.com/hibohiboo/items/2e01ab4427341acaccbe
[*2]:https://qiita.com/hibohiboo/items/5f64637f511c74e157c1
[*3]:https://qiita.com/y047aka/items/b1c9c056938786e6e602
[*4]:https://qiita.com/ymtszw/items/1cabbdbda4273b4c1978
[*5]:https://package.elm-lang.org/packages/NoRedInk/elm-decode-pipeline/3.0.1/