node.jsでmilkcocoaに保存してあるデータをGoogleSpreadSheetにイベントループで書き込んでみる。


## はじめに
こんにちは[LIG](http://liginc.co.jp)のエンジニア[のびすけ](http://liginc.co.jp/member/member_detail?user=nobisuke)です。

過去に書いた記事 ([StreamingAPIで取得したツイートをGoogleSpreadSheetにリアルタイムに書き込んでみました。](http://qiita.com/n0bisuke/items/ce0477c43205aff85c3b))をもとにmilkcocoaに保存してあるデータをGoogleSpreadSheetに書き込んでみます。
![](http://i.gyazo.com/1ba32518f8ce86d4aa69b84b223a99d8.png)

今回はストリーミングAPIやSocket.ioでは無く、通常にデータを取得 -> 書き込みをします。

## milkcocoaにnode.jsからアクセスしてみる

[フロント開発でメチャ活躍するMilkcocoaがサーバー側(Node.js)でも活躍してくれた。](http://qiita.com/n0bisuke/items/c6fa760e4bb480e95d2f)の記事を元にnode.jsでのmilkcocoaを使う準備をします。

```
var MilkCocoa = require('./milkcocoa');
var milkcocoa = new MilkCocoa('https://{your-app-id}.mlkcca.com');
var ds = milkcocoa.dataStore('dataStore');
var MAX_COUNT = 300; //決めうち

var query = ds.query();
query.limit(MAX_COUNT);
query.done(function(data){
 console.log(data);
});
```

* limitの値はデフォルトで1000件 (https://mlkcca.com/document/api-js.html)
* 現時点でdataStore内のデータ件数を取得するメソッドは未実装なため`MAX_COUNT`はいい感じの値を設定
* 参考: [milkcocoaのremoveメソッドとセキュリティルールで少しハマった件](http://qiita.com/n0bisuke/items/044889271251900c7a74)

## スプレットシートへ書き込みと生じた問題

###通常にループをまわしていると、前の行で書き込みをしている途中に次のループが実行されてしまい、同じ行が上書きされる場合があった
そのため、Jxckさんの記事([「for やめろ」またはイベントループと nextTick()](http://jxck.hatenablog.com/entry/for-with-eventloop))を参考にイベントループな感じにしてみました。

`sheetCtl()`のコールバック内で`process.emit()`を呼ぶことで1行書き込みが終わってから次の行へ書き込み処理を行う同期処理的な感じになります。

```gas.js
var Spreadsheet = require('edit-google-spreadsheet');
var MilkCocoa = require('./milkcocoa');
var milkcocoa = new MilkCocoa('https://{your-app-id}.mlkcca.com');
var ds = milkcocoa.dataStore('dataStore');
var MAX_COUNT = 300;
var query = ds.query();

var options = {
    debug: true,
    spreadsheetId: 'スプレットシートID',
    worksheetId: 'od6', // 1つめだったら、od6
    username: 'googleアカウントのメールアドレス',
    password: 'password'
};

function sheetCtl(spreadsheet, data, count, cb){
    spreadsheet.receive(function(err, rows, info) {
        if(err) throw err;
        
		//保存するデータオブジェクト 1列目にcount,2列目にdata.id, etc...
        var saveData = {
            1: count,
            2: data.id,
            3: data.hoge,
            4: data.text,
            5: data.test,
            6: data.created_at
        };

		 //最後の行数を取得
        var nextRow = info.nextRow;
        var output = {};
        output[nextRow] = saveData;
        spreadsheet.add(output);
        spreadsheet.send(function(err) {
            if(err) throw err;
            console.log("comp");
            cb(); //1行書き込みが終わった時点でコールバック
        });
    });

}

query.limit(MAX_COUNT);
query.done(function(data){

    Spreadsheet.load(options, function sheetReady(err, spreadsheet){
        //イベントループで実行
        process.on('gas', function(c) {
          if (c > MAX_COUNT) {
            return;
          }
          var item = data.shift();
          if(item.id){
            sheetCtl(spreadsheet, item, c, function(){
              console.log(c);
              process.emit('count', ++c);
            });
          }
        });

        process.emit('gas', 0); //実行

    });
    
});
```

実行

```
$ node gas.js
connected
Logging into Google...
Logged into Google
Retrieved 0 cells and 0 rows
Sending updates...
Successfully Updated Spreadsheet
comp
0
Retrieved 7 cells and 1 rows
Sending updates...
Successfully Updated Spreadsheet
comp
1

・
・
・
・
```

![](http://i.gyazo.com/776530267821c5aacacdbcb6fc8fec17.gif)

##まとめ
 非同期ループに若干ハマりました汗
 process.onで再起的に処理させるのは色々と使えそうです。