ES.next的にNode.jsでJSON-RPCする

気づいたら自分の投稿が合計100回もストックされていました。みなさんありがとうございます、あらっきぃです。

[method_missing を利用して動的にAPIのエンドポイントにアクセスする](http://qiita.com/items/8b45f51098fec6732dea) を読んで、ES.nextのProxyを使ってJSON-RPCを呼んだら面白そうな気がしたので実装しました。

ちゃちゃっと作ったものだし、そもそも似たようなライブラリがすでにありそう。

ちなみにJSON-RPCというのは、リモート環境にある関数をネットワークからJSONにデータを載せてでリクエストして、実行するプロトコルです。
Ideone APIとかで使えます。（というかそれでしか使ったことない）

```javascript:jsonrpc.js
var
request = require('request');

function JsonRpcClient(url) {
  return Proxy.create({
    get: function(_, method) {
      var
      requester = function() {
        var
        args = Array.prototype.slice.call(arguments, 0);
        return call.bind(this, method, args);
      };
      requester.call = function(params, callback) {
        call(method, params, callback);
      };
      return requester;
    }
  });
  
  function call(method, params, callback) {
    var
    body = JSON.stringify({
      jsonrpc: '2.0',
      id: +(new Date),
      method: method,
      params: params,
    });
    request(url, {
      method: 'POST',
      body: body,
      header: {
        'Content-Length': Buffer.byteLength(body, encoding='utf8'),
      },
      json: true,
    }, function(err, res, body) {
      if(body && body.error) {
        err = new Error(body.error.msg);
        err.data = body.error;
      }
      
      callback(err, body && body.result);
    });
  }
}

module.exports = {
  createClient : JsonRpcClient,
};
```

例として [ideone](http://ideone.com) のAPIを呼んでみます。
`user`と`pass`は自分のものを使ってください。

また、このコードを実行するためには上記の`jsonrpc.js`と同じディレクトリに置いて,
nodeに`--harmony-proxies`オプションを付けてください。

```javascript:ideone.js
var
async  = require('async'),
ideone = require('./jsonrpc').createClient('http://ideone.com/api/1/service.json');

var
user   = 'your user',
pass   = 'your pass',
lang   = {'python': 4, 'ruby': 17}['ruby'],
src    = 'puts "Hello, World!"',
input  = '';

/*
ideone.testFunction(user, pass)(function(err, res) {
  console.log(res);
});
*/

async.waterfall([
  function(next) {
    console.log('createSubmission');
    ideone.createSubmission(user, pass, src, lang, input, true, false)(next);
  },
  function loop(data, next) {
    var
    link = data.link;
    console.log('getSubmissionStatus: %s', link);
    ideone.getSubmissionStatus(user, pass, link)(function(err, res) {
      console.log(res);
      if(err){
        next(err);
        return;
      }
      if(res.status == 0){
        next(null, link);
        return;
      }
      setTimeout(function(){
        loop(data, next);
      }, 100);
    });
  },
  function(link, next) {
    console.log('getSubmissionDetails');
    ideone.getSubmissionDetails(user, pass, link, false, false, true, true, true)(next);
  },
], function(err, result) {
  if(err) {
    console.log(err);
    return;
  }
  console.log(result);
});
```

`ideone.createSubmission`のように、APIをメソッドのように呼び出していることに注目！

ところで`getSubmissionStatus`を呼び出して実行の終了を確かめている辺り、async.jsを使ってもっとスムーズに実装できたりしないんでしょうか？

##参考

  * [Old Proxy API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Old_Proxy_API?redirectlocale=en-US&redirectslug=JavaScript%2FOld_Proxy_API) Es.nextのProxyの詳細。残念なことにNode.jsでサポートされているのはOld Proxy APIなんです…。
  * [jabsorbのJSON-RPCインターフェイスにRubyで接続してみる - うなの日記](http://d.hatena.ne.jp/unageanu/20100103/1262488631) Rubyで`method_missing`を使って似たようなことをした例。参考にさせてもらいました。
  * [意外と便利なJSON RPC](TechTalkManiacs - http://d.hatena.ne.jp/kagigotonet/20121204/1354630831) JSON-RPCについてはここを見てください。