三連休なのでParseを試してみる(毎日更新)

三連休だし勉強用にアプリでも作ろうかなと思ったけど、サーバー側を作るのが面倒臭い。[Synth](http://www.synthjs.com)を試すのも悪くないなと思ったけど、まだ情報が少ない/特にACL周りの方法がいまいちよく見えなかった。で、いろいろ調べていたらmBaasの[Parse](http://parse.com)の評判が良かったので、試してみることにした。

備忘録的なメモ。毎日更新(予定)

# 一日目
## Parse
### MBaaS
* [Parse](http://parse.com)は[MBaaS](http://matome.naver.jp/odai/2138620499863058801)の一つ。MBaaS = アプリで必要となるようなサーバー側の機能をまるごと提供してくれるようなサービス。
* 他にもサービスはあるけど、iOS / AndroidだけでなくJavaScript用のSDKも用意されていて、かつ評判が大分よかったので、これを試すことにした。

### できること
#### RESTful APIの自動生成的な
* クライアント(アプリ)からAPIを叩くことで、サーバーにあるデータベースのレコード作成/編集/削除/検索。データの型を定義するだけで、RESTful APIを自動的に作ってくれるイメージ。
* ちょっとコードを書けば、作成時のバリデーションを実装したり、オブジェクト以外の結果(例えば平均値とか)を返すAPIも追加できる。

#### その他
* その際、アクセス権限管理/ユーザの管理できたり。
* ユーザに対してプッシュ通知送ったり、いろいろ。

### 第一印象
* サイトわかりやすい。登録フローもわかりやすいし、ダッシュボードもわかりやすい、ドキュメントも親切。
* 有料プラン(月199ドル)は高いけれども、どうせ個人でサービス作っても無料利用上限にぶつかるほどヒットしないし(震え声)

## Androidで試してみた
### SDKのインストール
* 公式サイトからダウンロードできるSDKのjarファイルをプロジェクトに突っ込む。Android Studioでも問題なし。
* アプリ起動時にParseライブラリの初期化を行って、アクセスのためのKeyとかを渡す。そのための初期化コードはダッシュボード上で確認できる。

### モデルつくる(Data Browser)
* Parse.comのダッシュボードのData Browserからモデルの定義(SQLのテーブルのスキーマ定義)できる。phpMyAdminみたいなUIで簡単に登録できる。
* Data Browser上ではレコードの追加削除編集もできる。

### モデル取得する

```
ParseQuery<ParseObject> query = ParseQuery.getQuery("Movie");
query.findInBackground(new FindCallback<ParseObject>() {
    public void done(List<ParseObject> movieList, ParseException e) {
        if (e == null) {
            Log.d("", "Movies " + movieList.toString());
        } else {
            Log.d("", "Error: " + e.getMessage());
        }
    }
});
```

* モデルを定義しておけば、アプリ側からはそのモデル名を指定するだけでレコードを取得したり保存したりできる。
* **特に何もしてないと完全オープンで、誰でもすべてのデータにアクセス/編集できてしまうので注意**。
* レコードの追加/削除などのAPIも直感的だし[ドキュメント](https://www.parse.com/docs/android_guide#objects)も親切で、すごくわかりやすい。すばらしい。

## HTML(JavaScript)から試してみた
* アプリからのSDK経由でのデータアクセスが素晴らしく簡単だったので、そのままアプリを作ってしまってもよかったのだけど、マスターデータをどうやって管理するか、という問題に直面。
* Data Browserでできないこともないのだけど、件数が多い/モデル間のリレーションがあるような場合は限界がある。
* なので、HTMLで管理画面作りたい。
* アプリとHTMLと両方からスムーズにアクセスできていいね。

### JavaScript SDK
* HTML + JavaScriptでアクセスする場合のSDK + テンプレートが用意されていて、ダッシュボードからダウンロードできる。
* 使い勝手はAndroidと変わらない。

### UIつくるのがめんどい -> AngularJS
* データの取得自体はJavaScript SDKでも簡単にできるのだけど、取得したデータをHTML上で整形して表示するのが面倒臭い。(Parseの問題ではなく、JS的な話で)
* なので、一年ぶりくらいにAngularJS使おうとしたが、大分忘れていて手間取った。

```
var Video = Parse.Object.extend("Video");
var query = new Parse.Query(Video);
query.find({
  success: function(results) {
    $scope.data.videos = results;
  }, 
  error: function(error) {
    alert("Error: " + error.code + " " + error.message);
  }
});
```

みたいに書いたけど、全然表示が更新されないので、`$scope`周りの仕様/使い方を勘違いしているんじゃないかと思って勉強しなおした。結局、表示が更新されない理由は[AngularJS のデータバインドを支える $apply](http://angularjsninja.com/blog/2013/12/27/angularjs-apply/)にあるとおり、ParseSDKのコールバック内で内容を更新しても`$digest`サイクルが回らないからだった。コールバックで受け取ったデータをDOMの方に反映させたい場合は明示的に`$scope.$apply()`を呼んでやる必要があった。ここで大分はまってしまった。

この問題乗り越えたら、大分サクサク進むようになった。AngularJSはCRUDアプリのUIを作るのに最適だし、ParseはCRUDアプリのバックエンドを作るのに最適。ちなみにAngularJSには[$parse](https://docs.angularjs.org/api/ng/service/$parse)というサービスがあるみたいだが、情報がほとんどみつからなかったので今回は使わなかった。

### Grunt導入
* 大分久しぶりにHTML/JSを書いていたが、長時間書いていたら「閉じカッコを書く」とか「エディタとブラウザを行き来する」とか辛くなってきたので、Grunt導入することにした。これも1年くらい触ってなかったので、いろいろ忘れていて苦戦した。
* やっぱり`livereload`が便利。ブラウザ行き来しなくてよくなる。
* `jade`と`coffee`を導入したかったけど、今日中にはできなかったので明日以降。

#### デプロイについて
* `grunt-rsync`使うと、一発でデプロイできるのでラク。
* フロントエンド部分しかデプロイしないので、デプロイが気軽にできていい。
* クロスドメインの問題とかあるので、ローカルでは動くけどサーバーじゃ動かなかった、みたいな話があったら怖いので、デプロイは最初のうちから気軽にできるようにしておくべき。

## 一日目の感想
* データ構造決めるだけで、バックエンドが動く状態になっているっていうのは新鮮で、いかに今までクリエイティブじゃない部分の作業が多かったか実感した。herokuを使った時にも、それまでのサーバー構築コストを実感したけれども、今回はそのさらに次のステップだなという感じ。サーバー構築/調整って時間がかかるからちゃんと動くようになった時に達成感を感じてしまうのだけど、そこで消費してしまう労力でかいからね。
* どちらかというと、AngularJSとGruntでハマってしまった。。。
* まだセキュリティ関連/カスタムAPIを試せていないのでなんともいえないけど、結構使えそう。自分でゼロからAPIサーバー設計する場合にくらべて、あるいはすでにあるシステムに比べて、あれができないこれができない、っていうのはいくらでも指摘できると思うけど、思想が違うからある意味できないことを見つけるのが簡単なのは当然。
* 今までのスタイルではなく、この枠組みにあわせて実用的なものを作っていくことができるのか、が焦点。正直、全部のニーズには対応できないんだろうなとは思うので、スピード/コストとできないことのトレードオフだけど、検討の価値はありそう。

## 明日以降の課題
* セキュリティ面の対策。
* jadeとcoffeeいれて効率あげる。

# 二日目
## Jade
* [Using Yeoman and jade](https://gist.github.com/passy/5229305) に従ってJadeを取り入れてみる。
* jadeタスクのsrcはこのドキュメントのままだとルートディレクトリにあるjadeファイルのみが対象となってしまって、view以下のjadeファイルがコンパイルされないので、変更する必要があった。

```
    # Jade
    jade:
      dist:
        options:
          pretty: true
        files: [
          {
            expand: true,
            cwd: '<%= yeoman.app %>',
            dest: '.tmp',
            src: [
              '*.jade'
              "views/{,*/}*.jade"
            ]
            ext: '.html'
          }
        ]
```

* htmlからjadeへの変換は[HTML 2 Jade](http://html2jade.aaron-powell.com)でできた。ただし、`!!! 5`はdeprecatedになっていて今は使えなかったので、その部分だけ`doctype`に直す必要があった。あと、インデントが一部おかしかったので、手動で直した。

## Cloud Code
### Cloud Functions
* 新しいAPIを追加できる(RESTfulでないAPIも追加できる)
* RESTの各Actionのbefore/afterのフィルターを実現できる。(beforeSaveでバリデーションしたりとか)
* jsで書く。

### 試す
* 試しにCloud Codeを使ってモデルのDeleteを禁止してみる。ACLを使って制御する方法もあるけど、今回はCloud Codeの勉強のためにCloud Codeで実装してみる。
* [Cloud Code Guide | Parse](https://www.parse.com/docs/cloud_code_guide)
* Guideに従って、Command Line Toolをインストールする。

モデル削除前のコールバックは[Running Code On Delete](https://www.parse.com/docs/cloud_code_guide#functions-ondelete)に説明があった。

> You can run custom Cloud Code before an object is deleted. You can do this with the beforeDelete method. For instance, this can be used to implement a restricted delete policy that is more sophisticated than what can be expressed through ACLs. For example, suppose you have a photo album app, where many photos are associated with each album, and you want to prevent the user from deleting an album if it still has a photo in it.

モデル削除前のチェックはbeforeDeleteメソッドを使って実現できるとのこと。

> If response.error is called, the Album object will not be deleted, and the client will get an error. If response.success is called, the object will be deleted normally. Your code should call one of these two callbacks.

beforeDeleteメソッドを定義して、その中で`response.error`を呼ぶとリクエストされたモデルの削除は行われず、`response.success`を呼ぶと削除されるとのこと。

```
Parse.Cloud.beforeDelete("Video", function(request, response) {
  response.error("This action is prohibited.");
});
```

たしかに、API叩いてもdeleteできなくなった。

## セキュリティ
* [Security](https://www.parse.com/docs/data#security)に詳細な説明がある。

### 概要
* アプリが使うIDとKEYはSecretではない。(そもそもJavaScriptで使う場合はオープンだし)
* APIは全部HTTPSだからMITM攻撃については気にしなくてよい。
* デフォルトの設定だと、クライアントアプリの権限で「クラスの作成」「クラスの構造変更」「オブジェクトに関する操作」なんでもできる。
* これらの権限はユーザ単位 or ロール単位で設定できる。
* 「クラスの作成」はアプリ設定から禁止にできる。
* 「クラスの構造変更」「オブジェクトに関する操作」の各権限は、Data Browser上で設定できる。

## 二日目の感想
* **効率向上のためのJade導入に予想以上に苦戦して、肝心なParseの勉強が進まなすぎた** (よくあること)
* ので、Jadeのlivereload/自動コンパイル設定ができた段階で、coffeeの導入は今日は断念。
* やっぱり、Parseは**ドキュメントの親切さとダッシュボードUIのわかりやすさが素晴らしい**。

## 明日以降の課題
* ユーザの仕組みを使ってみる。で、管理ユーザだけモデルの削除できるようにする。

# 三日目
* 気分転換に丹沢にいって山登ってたら野生の鹿の親子にあった。

## UserのSign Up / Log Inについて
* [ドキュメント](https://www.parse.com/docs/js_guide#users)通りで、あまり特別なことなかった。
* どちらかというとYeomanのAngularJS用テンプレートの構造上の話で、AngularJSのControllerをまたいでログイン情報を共有するのをどう実現するかで少しはまった。
  * http://stackoverflow.com/questions/11252780/whats-the-correct-way-to-communicate-between-controllers-in-angularjs 
  * http://qiita.com/sunny4381/items/aeae1e154346b5cf6009

# まとめ
* 本当にParseの機能だけで、実用的なサービス作れるか、というのを試したかったんだけど、Parse自体の使い勝手というより周辺環境(Grunt周り、AngularJS周り)で大分時間を取られてしまって、検証しきれなかった。
* まあただ、初日に気がついた **スマホアプリのバックエンドとして使うには機能として十分そうなんだけど、なんらかの形で管理機能(管理画面)を実装しないといけない** という点が実際結構でかい。JSに強い人だったらサクッとかけるのかもしれないけど、そうじゃないとこれは少し重いタスクになりうる。
* 引き続き検証してくかな。