CLOVER🍀

That was when it all began.

Infinispan JavaScriptクライアントで以前書いたサンプルを、Babel(ES2015)、Mocha、Chaiで書き直してみた

以前に書いた、こちらのエントリ。

InfinispanのJavaScriptクライアント(Hot Rod)を試してみる - CLOVER

InfinispanのJavaScriptクライアントを使って、チュートリアルを参考に書いてみたエントリなのですが、どうにもPromiseの扱いが読みづらくて書き直してみようかなと。

書き直すにあたり、こういうコンセプトでいくことにしました。

  • あんまりPromiseをネストさせない(前がすごく読みづらかったのは、これが大きい)
  • Babelを使ってECMAScript 2015で書く
  • テストコードとして実行する(Mocha+Chai)

というわけで、参考にしたり使ったりしたライブラリは、このあたり。

GitHub - infinispan/js-client: Javascript client for Infinispan, over the Hot Rod wire protocol

JavaScript Promiseの本

Babel

Mocha

Chai

準備

必要なnpmモジュールをインストールします。

$ npm install --save-dev infinispan mocha chai babel-register babel-preset-es2015

依存関係は、こんな感じで。

  "devDependencies": {
    "babel-preset-es2015": "^6.6.0",
    "babel-register": "^6.7.2",
    "chai": "^3.5.0",
    "infinispan": "^0.2.0",
    "mocha": "^2.4.5"
  }

また、Mochaでテストを動かす前にBabelを使うので、Babelのドキュメントを設定にこのように設定。

  "scripts": {
    "test": "mocha --compilers js:babel-register"
  },

.babelrcも用意。

$ echo '{ "presets": ["es2015"] }' > .babelrc

テストの実行は、以下のコマンドになります。

$ npm run test

テストコードの雛形

テストコードの全体の枠としては、以下のように作成。
test/infinispan-js-client-test.js

const infinispan = require("infinispan");
const should = require("chai").should();

describe("Infinispan JavaScript Client, Getting Started Test", () => {
    // ここに、テストを書く!
}

とりあえず、書き直してみる

何も考えずに、前に書いたコードをECMAScript 2015+Mocha+Chaiで書き直すと、こんな感じに。
※clientメソッドの引数は、前のバージョンから指定方法が変わったようです

    it("minimal.", () => {
        return infinispan.client({ host: "localhost", port: 11222 }).then(client => {
            console.log("connected.");

            // put
            return client.put("key1", "value1")
                // get and verify
                .then(() => client.get("key1").then(value => value.should.equal("value1")))
                // replace
                .then(() => client.replace("key1", "value100"))
                // put
                .then(() => client.put("key2", "value2"))
                // get and verify
                .then(() => client.get("key1").then(value => value.should.equal("value100")))
                // get and verify
                .then(() => client.get("key2").then(value => value.should.equal("value2")))
                // put all
                .then(() => client.putAll([
                    { key: "key3", value: "value3" },
                    { key: "key4", value: "value4" },
                    { key: "key5", value: "value5" }
                ]))
                // get all and verify
                .then(() => client.getAll(["key1", "key2", "key3", "key4", "key5"]).then(entries => {
                    entries.length.should.equal(5);

                    const sorted = entries.sort((a, b) => a.key.substring(3) - b.key.substring(3));
                    sorted[0].should.deep.equal({ key: "key1", value: "value100" });
                    sorted[1].should.deep.equal({ key: "key2", value: "value2" });
                    sorted[2].should.deep.equal({ key: "key3", value: "value3" });
                    sorted[3].should.deep.equal({ key: "key4", value: "value4" });
                    sorted[4].should.deep.equal({ key: "key5", value: "value5" });
                }))
                // clear
                .then(() => client.clear())
                // size and verify
                .then(() => client.size().then(size => size.should.equal(0)))
                // disconnect
                .finally(() => {
                    console.log("disconnect.");
                    return client.disconnect();
                });
        });
    });

Promiseをthenでつなげていくスタイルですが、getしたものについてはそのままthenでアサーションするようにしています。

前に書いたコードよりは、コールバックがあまり深くならず、だいぶ見やすくなった気がします(本人目線)。

なお、前回はこれでした。

var infinispan = require("infinispan");

var connected = infinispan.client(11222, "localhost");

connected.then(function(client) {
    console.log("connected.");

    var putted1 = client.put("key1", "value1");
    var getted1 = putted1.then(function() {
        return client.get("key1").then(function(value) {
            console.log("key1 = " + value);
        });
    });

    var replaceAndPut = getted1.then(function() {
        return client
            .replace("key1", "value100")
            .then(function() {
                return client.put("key2", "value2");
            });
    });

    var getted2 = replaceAndPut.then(function() {
        return client
            .get("key1")
            .then(function(value) {
                console.log("key1 = " + value);
            })
            .then(function() {
                return client.get("key2").then(function(value) {
                    console.log("key2 = " + value);
                });
            })
    });

    var putAll = getted2.then(function() {
        return client.putAll([
            { key: "key3", value: "value3" },
            { key: "key4", value: "value4" },
            { key: "key5", value: "value5" }
        ])
    });

    var getAll = putAll.then(function() {
        return client
            .getAll(["key1", "key2", "key3", "key4", "key5"])
            .then(function(entries) {
                console.log("entries = " + JSON.stringify(entries));
            });
    });

    return getAll.then(function() {
        console.log("clear");
        return client.clear();
    }).finally(function() {
        return client.disconnect().then(function() {
            console.log("disonnected");
        });
    });
});

アサーションも次のthenで

先の結果だと、前の関数の結果を次の関数で引き継げないようにも見えてしまうので、アサーションを次のthenに与えた関数で実行するようにもうひとつパターンを書いてみました。

    it("pass callback value.", () => {
        return infinispan.client({ host: "localhost", port: 11222 }).then(client => {
            console.log("connected.");

            // put
            return client.put("key1", "value1")
                // get
                .then(() => client.get("key1"))
                // get result verify
                .then(value => value.should.equal("value1"))
                // replace
                .then(() => client.replace("key1", "value100"))
                // put
                .then(() => client.put("key2", "value2"))
                // get
                .then(() => client.get("key1"))
                // get result verify
                .then(value => value.should.equal("value100"))
                // get
                .then(() => client.get("key2"))
                // get result verify
                .then(value => value.should.equal("value2"))
                // put all
                .then(() => client.putAll([
                    { key: "key3", value: "value3" },
                    { key: "key4", value: "value4" },
                    { key: "key5", value: "value5" }
                ]))
                // get all
                .then(() => client.getAll(["key1", "key2", "key3", "key4", "key5"]))
                // get all result verify
                .then(entries => {
                    entries.length.should.equal(5);

                    const sorted = entries.sort((a, b) => a.key.substring(3) - b.key.substring(3));
                    sorted[0].should.deep.equal({ key: "key1", value: "value100" });
                    sorted[1].should.deep.equal({ key: "key2", value: "value2" });
                    sorted[2].should.deep.equal({ key: "key3", value: "value3" });
                    sorted[3].should.deep.equal({ key: "key4", value: "value4" });
                    sorted[4].should.deep.equal({ key: "key5", value: "value5" });
                })
                // clear
                .then(() => client.clear())
                // size
                .then(() => client.size())
                // size result verify
                .then(size => size.should.equal(0))
                // disconnect
                .finally(() => {
                    console.log("disconnect.");
                    return client.disconnect();
                });
        });
    });

ちょっと長くなってしまいましたが、ちゃんと直前のPromiseで得た結果が次の関数に渡せています。

Promiseをつなげるにあたって

JavaScript界隈でPromiseをどう扱うのが今のスタイルなのか、よくわかっていません。

ちょっと調べて、highlandやbluebirdを使うのかなとも思ったのですが、今回はうまく使えず挫折しました…。

Node.jsフロー制御 Part 1 – コールバック地獄 vs. Async vs. Highland | POSTD

Getting Started | bluebird

Highland.js

素でPromiseを使うよりも、楽な方法ってあるでしょうか?

なお、Infinispan JavaScriptクライアントで使っているPromiseは、どうやらこちらのようです。

promise - npm

GitHub - then/promise: Bare bones Promises/A+ implementation

まとめ

以前に書いたInfinispanのJavaScriptクライアントを使ったコード例を、ECMAScript 2015を使ったりテストコードとして書き直してみました。

InfinispanのJavaScriptクライアントのAPIの最新化についてはまだちゃんと追っていないので、そのうち。

今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/infinispan-getting-started/tree/master/remote-js-client-with-es2015-rewrited