「node.js」と一致するもの

MeteorでJadeのススメ

  • 投稿日:
  • by

Meteorのお話です。

Node.jsではExpressでよくjadeを使っています。

http://jade-lang.com/

なぜかjadeは好きです。RubyでHamlは使ったことがありますが、それほど好感を持った覚えはありません。なぜでしょう、%があると見栄えが悪いからでしょうか。

Meteorにもjadeのパッケージを作った人がいるようです。

https://atmospherejs.com/mquandalle/jade

これが思いのほか出来がよくて、気に入りました。 単にjadeの変換を再現しただけではなくて、Meteor用にチューンされている感じです。

インストールは、

$  meteor add mquandalle:jade

とするだけで、*.jadeファイルで書けばあとはよろしくやってくれます。上記ページに使い方は書いてありますが、いくつか転記します。

まず、テンプレート呼び出しの

{{> hello}}

は、

+hello

になります。最初はちょっと分かりにくいと思いました。慣れれば普通です。

次に、if文は、

{{if hoge}}
  <div>hello</div>
{{else}}
  <div>bye</div>
{{/if}

が、

+if hoge
  div hello
+else
  div bye

になります。この接頭辞+は省略もできるようですが、個人的には残しておいたほうが制御ブロックであることが見た目で分かりやすくてよいと思いました。else ifも独自実装しているようです。シンタックスシュガーとして。

最後にまだ実装されていないと書いてありますが、anonymous helperというのが載っています。

+if len > 0

みたいなことが書けるようになるそうです。これはとても実装されて欲しい機能です。これだけのためにhelperを書くのは手間でしょう。

以上、Meteorでjadeがすんなり使えるというお話でした。

ついでに、jade系で気になるツールを紹介します。

https://github.com/donpark/html2jade

HTMLをjadeに戻せるようです。まだ試していませんが、 これはいいですね。過去に書いたHTMLをjade化したくなります。 もし安定して使えるなら、人が書いたHTMLを一度jadeにしてから 編集して、再度HTMLに戻すということもできるかもしれません。

さらに、余談ですが、html2jadeを検索していたら、

https://github.com/vidalab/meteor-html2jade

を見つけました。何かと思ったら、html2jadeをwebで使えるようにしたMeteorアプリのようです。

過去にKarma(Testacular)を使ったことはありますが、今回やっと時代に追いついてProtractorを使ってみました。特にPhantomJSを使ってheadlessでE2Eテストするときに気づいたことをまとめます。

Protractorは、 Seleniumの WebDriverJSのラッパーで、AngularJSのE2Eテストのためのフレームワークです。

https://docs.angularjs.org/guide/e2e-testing

がエントリーポイントになるでしょう。 しかし、これだけ読んでも最終的な実行方法は分かりづらいです。 今回の目的は、コマンド一つ、例えば、

$ npm run e2e-test

でE2Eテストを実行してするための環境セットを作ることです。また、headlessで動かしたかったので、典型的に使われるChromeではなく、PhantomJSを前提にしました。サーバはExpress/Node.jsの想定ですが、それ以外の場合にも応用できるかと思います。

初めによくある環境設定です。

$ npm install protractor
$ npm install phantomjs
$ ./node_modules/.bin/webdriver-manager update

この例ではローカルディレクトリにモジュールをインストールしていますが。グローバルにインストールしたい人はそのようにどうぞ。

まず初めにはまったことはwebdriverが起動しないことでした。

$ ./node_modules/.bin/webdriver-manager start
INFO - Launching a standalone server
WARN - null
java.net.SocketException: No such device
    at java.net.NetworkInterface.isLoopback0(Native Method)

このようなエラーがでました。ワーニングかと思って無視していたらいけませんでした。どうやら、IPv6のアドレスのみを使っていてうまく行かないようだったので、Javaのオプションを追加して次のようにするとうまく行くことが分かりました。

$ env _JAVA_OPTIONS=-Djava.net.preferIPv4Stack=true ./node_modules/.bin/webdriver-manager start

さて、Protractorのチュートリアルには、webdriverをコマンドラインで手動で実行するように書いてありますが、個人的にはこれは面倒に感じます。時間はかかりますが、毎回自動で起動して欲しいです。これは簡単で、protractor.conf.jsにseleniumAddressを書かなければよいだけでした。下記に、protractor.confを載せます。

exports.config = {
  specs: [
    '*-spec.js'
  ],
  capabilities: {
    'browserName': 'phantomjs',
    'phantomjs.binary.path': './node_modules/.bin/phantomjs'
  },
  baseUrl: 'http://localhost:' + (process.env.PORT || 3000) + '/'
};

これだけで大丈夫でした。baseUrlは、specにURLをフルパスで書かなくてもよいようにするためで、expressが下記のように起動されている想定です。

app.listen(process.env.PORT || 3000);

specの細かい内容は今回は書きませんが、もっとも単純なものは例えば次のようになります。

describe('tutorial', function() {
  it('should have a title', function() {
    browser.get('index.html');
    expect(browser.getTitle()).toEqual('The Title');
  });
});

ところで、Protractorはテストのフレームワークであるもののアプリの起動には関与せず、アプリは自分で起動しなければいけません。また、テスト終了後はアプリも終了したいです。今回はお手軽にシェルスクリプトを書きました。

#!/bin/sh

export _JAVA_OPTIONS=-Djava.net.preferIPv4Stack=true
export PORT=12345
node ./app.js &
PID=$!
./node_modules/.bin/protractor ./e2e-test/protractor.conf.js
kill $PID

このスクリプトをpackage.jsonのscriptsに書いておけば、当初の目的であったnpmで一発でE2Eテストを走らせることができました。

Happy e2e testing!

自分が興味あるものの一つにコミュニケーションツールがあります。 と言っても普通のコミュニケーションではないです。 例えば、見知らぬ人と一時的なつながりを感じられるようなそんなツールがいいと思います。言語も使わず、世界のどこかの誰かと。

FacebookのPokeはよかったです。1bitで行うコミュニケーションとでも呼びましょうか。日本では「あいさつ」となり、意味が分からなくなりました。 ちなみに、Pokeは一度機能が隠れてしまったのですが、また復活しました。 最近は、Yoが流行っているらしいです。これも1bitコミュニケーションだと思います。どこまで流行るのか、気になるところです。

流行っているついでに、Snapchatについても気になるところです。 実は使ったことありませんが、これは使い捨てコミュニケーションだと思っています。 使い捨てコミュニケーションができたら面白いだろうなぁとは思っていましたが、 まさかそんなものが流行るとは想像していませんでした。 写真だからうまくいったのでしょうか。

あとは、匿名コミュニケーションにも興味あります。 最近は実名指向で、あまり匿名を重視したコミュニケーションツールは登場していないのでしょうか。

と言うわけで、前置きが長くなりましたが、1bitで使い捨てで匿名のコミュニケーションツールを作ってみようと思いました。 1bitで伝えたいことはなんだろう、と考えたその日が暑い日だったので、「暑い」にしました。 「暑い」を伝え合うだけでは、どんどん暑苦しくなるだけですので、 ストレス発散をできるような「吹き飛ばし」機能をつけることにしました。

atsufut_title.png

これがタイトル画面です。

atsufut_main.png

これがメイン画面です。これだけです。 画面の上半分をクリック(タップ)すると、丸が落ちてきます。 何度も押すと、丸がたまってきます。 次に画面の下半分をクリックすると、丸を吹き飛ばします。 それだけです。

それでは、やってみましょう。

http://atsufut.axlight.com/

このURLをスマホで開きましょう。PCでも大丈夫です。モダンブラウザ必須です。

一人でやると、すぐに飽きます。 複数人で同時にやるとまあまあ面白いとは思います。 しかし、たまたま同時に開いている人がいることは稀です。 ここをどうするかが悩みどころです。

今回、開発に利用したライブラリは、 node.js, socket.io, angular.js, famo.us あたりです。

自作のライブラリsocial-cms-backendでは、socket.ioのmiddlewareが使いたかったのでv1.0を先取りして使っていました。

socket.ioのv1.0はengine.ioという低レイヤから書き直しているらしいですが、一向にリリースされないなぁと思っていました。計画も分かりません。

socket.ioのの代替としてSockJSがあることを知りました。メインサイトのhttp://sockjs.org/がhttps://github.com/sockjs/sockjs-clientにリダイレクトされるのがなんかさびしいですが、そのうちかっこいいページを作るつもりなのでしょうか。

比較記事を探しましょう。

http://www.quora.com/Sock-js/What-are-the-pros-and-cons-of-socket-io-vs-sockjs

両方を実装した経験というのは説得力あります。 SockJSはWebSocketのAPIを中心にしていてシンプルなようですね。 今のところコミュニティもアクティブだそうで、なによりです。

http://stackoverflow.com/questions/22134061/engine-io-or-sockjs-which-one-to-choose

こちらもいい質問です。回答は集まっていませんが。 WebSocketを前提としてない場合はemulationするSockJSか、全ブラウザで動く確実な方式からアップグレードするengine.ioか、方針が逆ですね。時代の流れとしてはSockJSの方がいい気もします。

さて、SockJSのnodeのサーバはというと、 https://github.com/sockjs/sockjs-node ですね。 しかし、中身を読もうとしたら、、、

CoffeeScript

でした。うーん、なぜかワクワクしない。

もともとnode.jsを始めたきっかけもsocket.ioを手軽に使いたかったからなので、socket.ioも応援したいところです。と思って、https://github.com/learnboost/socket.ioを久しぶりに見てみると、おお、最近コミットがあるじゃないですか、1.0.0-preとかなっていていい感じです。ちょっと期待できるかなと思いつつも、このあとコミュニティがアクティブになるかどうかが気になります。

というわけで、しばらく様子を見ましょう。

facebookはアプリを開発するときにテストユーザを作ることができます。これは非常に便利で、複数のユーザでの操作などもテストできます。

これまで、facebookアプリ(というかライブラリ)を自動でテストするときにテストユーザも自動で作っていました。過去記事を参照。他にも例えば、Qiitaの投稿に手順が載っています。

Facebookアプリのテストユーザーの作り方手順

ところが、このlogin_urlが使えなくなっているのに気づきました。404エラーになってしまうのです。Google検索すると、

http://stackoverflow.com/questions/22583963/404-error-trying-to-use-facebook-test-user-login-url

がヒットしました。今回は、stackoverflowでいい投稿を見つけました。すばらしい。stackoverflowは便利なときは便利です。facebookにバグ報告していて、

https://developers.facebook.com/bugs/276245435872240/

これによると、ポリシーが変わって使えなくなったと。「ドキュメントもアップデートしてくれ」と要望がでてますね。さて、stackoverflowのAnswerにはworkaroundも出ていて、簡単に回避できました。この方法が止められなければいいのですが。

参考までに、node.jsでのテストユーザのログインのコードの抜粋を載せておきます。

request.get('https://www.facebook.com', function() {
  request.post('https://www.facebook.com/login.php', {
    followAllRedirects: true,
    form: {
      email: facebook_user_email,
      pass: facebook_user_password
    }
  }, function(error, response) {
    //check response
  });
});

完全なコードはこちらです。

nodeのchild_processにはちょっと紛らわしい3つのメソッドがあります。 execとexecFileとspawnです。

いきなり話題がそれますが、forkも紛らわしいですよね。システムコールのforkを連想させるのに、中身は違うので。forkはどちらかと言うと、spawnのラッパーみたいな感じです。

本題に戻ります。google検索すると、execとspawnの違いはいっぱい引っかかります。例えば、これ。

http://www.hacksparrow.com/difference-between-spawn-and-exec-of-node-js-child_process.html

ところが、execFileとの比較は極端に少ないです。どうやら、昔はexecFileはundocumentedだったようですね。

https://github.com/joyent/node/issues/1700

一言で言うと、spawnが自由度が高いメソッドで、execはそのラッパーです。execFileはその中間とでも言いましょうか。execとexecFileの最大の違いは、execは/bin/shを経由してプロセスを起動することです。

nodeのソースを見ると分かりますが、execはexecFileのシンプルなラッパーになっています。また余談ですが、v0.10.18のソースと比較すると少しリファクタリングされて分かりやすくなっています。

個人的には、通常のコーディングではexecFileを使う方がベターだと思います。/bin/shを介さないからです。多くの場合、コマンドラインのスペース区切りを配列に変換するだけのために/bin/shを使っているのではないでしょうか。「通常」というのは誤解を招くかもしれませんが、webのserver sideプログラミングをするという程度の想定です。逆に、execが便利なケースとしては、linux上でちょっとしたスクリプトを書きたい場合ですかね。bashとかperlの代わりに、という感じ。/bin/shのパワーを使いたい場合ですね。例えば、

child_process.exec("cat foo | sort | uniq > bar");

とか。

まとめると、spawnは柔軟なメソッド、execFileはBufferを戻り値にして簡単に扱えるようにしたメソッド、execはスクリプト用の簡易メソッド、という感じですね。というわけで、おすすめはspawnかexecFile、ということで。

node-http-proxyというのはnode.jsでhttpプロクシを作るためのライブラリです。 WebSocketsに対応しているリバースプロクシが作れるということで注目していました。

さて、気づいたら、いつの間にかバージョンアップしていました。

http://blog.nodejitsu.com/node-http-proxy-1dot0/

node-0.10用に書き直してv1.0になったということです。 ところが、以前のバージョンで使えていたProxyTableは使えなくなっていて、 userlandでコーディングしてねということでした。 以前のバージョンではほとんどコーディングしなくても使えるようになっていて それはそれでよかったのですが、v1.0ではシンプルなAPIとなっていて 基本的にコーディングして使うモジュールという位置づけになっています。 個人的にはv1.0の方が好みです。

ProxyTableはないけれど簡単できるよ、というサンプルが上記ブログに乗っていて、 ホスト名で振り分ける例が載っています。 ただ、前のProxyTableはパスでも振り分けられたのです。 完全互換というわけではありませんが、パスで振り分けるサンプルを紹介します。 ついでに、WebSocketsもHTTPSもどんな組み合わせでも大丈夫なように。

というわけで、コードです。

var fs = require('fs');
var http = require('http');
var https = require('https');
var httpProxy = require('http-proxy');

var proxyTable = {
  "/path1/": "http://localhost:1234/",
  "/path2/": "http://localhost:2345/",
  "/sslpath3/": "https://localhost:3456/",
  "/sslpath4/": "https://localhost:4567/"
};

var sslOptions = {
  key: fs.readFileSync('key.pem', 'utf8'),
  cert: fs.readFileSync('cert.pem', 'utf8')
};

var proxy = httpProxy.createProxyServer();

function makeHandler(type) {
  return function(req, res, head) {
    for (path in proxyTable) {
      if (req.url.lastIndexOf(path, 0) === 0) {
        if (type === 'web') {
          proxy.web(req, res, {
            target: proxyTable[path]
          })
        } else if (type === 'ws') {
          proxy.ws(req, res, head, {
            target: proxyTable[path]
          })
        } else {
          res.writeHead(500);
          res.end();
        }
        return;
      }
    }
    res.writeHead(404);
    res.end();
  };
}

var proxyServer = http.createServer(makeHandler('web'));
proxyServer.on('upgrade', makeHandler('ws'));
proxyServer.listen(80);

var sslProxyServer = https.createServer(sslOptions, makeHandler('web'));
sslProxyServer.on('upgrade', makeHandler('ws'));
sslProxyServer.listen(443);

いかがでしょう?

モバイルファーストの次はオフラインファーストってことで、前から作ってみたいと思っていたアプリを作ってみました。

アプリ自体は「メモ」アプリで特に目新しいものではありません。機能も単純です。

ですが、BMEANスタックで作ったことが新しいかと思っています。 BMEANとは、

を利用するソフトウエアスタックです。

今回、「Railsより簡単にTwitterクローンを作るためのnode.jsのライブラリ」を目指したsocial-cms-backendを改良してBreezeをサポートする機能を追加しました。そのため、サーバ側のコードはほとんど書かずにアプリを作ることができます。また、HTML5のキャッシュマニフェストを使うために、connect-cache-manifestも利用しています。

アプリは、OpenShiftを使って配置しました。ぜひ、一度お試しください。

http://notesappsample-nodeangularapp.rhcloud.com/

左上にLoginがありますが、Loginしなくても使用できます。 その場合、localStorageにデータが保存されます。 LoginするとSyncが使えるようになり、サーバ側にデータを保存することができるため、複数のデバイスでデータの同期ができます。

iPhoneのWebアプリにもなります。「ホーム画面に追加」してください。スプラッシュイメージがうまく表示されないのですが、これは深追いしていません。

ソースコードはこちらです。よろしければ見てみてください。細かいところは、Breezeとsocial-cms-backendがやってくれるので、だいぶシンプルになっていると思います。

https://github.com/dai-shi/notes-app-sample

BMEANスタックというのは、BreezeJS, MongoDB, Express, AngularJS, Node.jsを使ったソフトエアスタックのことです。

Breezeを簡単に使いたいと思って、social-cms-backendを大幅に改修しました。 APIは変わっていないのですが、breeze-mongodbを使えるように工夫しました。反面、処理が複雑になりオーバヘッドが増えています。

その新しいsocial-cms-backendを使う段階で一つ気づいたことがあるので、メモ代わりに書いておきます。詳細は省きますが、クライアントサイドのコードでpromiseを使いたくなりました。そもそも、BreezeのAPIがpromiseを返します。Angularにも$qというのがあってpromiseが使えます。

ところが、Breezeのpromiseのメソッドが、

promise.then()
promise.fail()
promise.fin()

なのに対して、Angularの方は、

promise.then()
promise.catch()
promise.finally()

になっているのです。ちなみに、catch,finallyはjsBeautifierでは予約語とみなされて不便です。

wrapすればいいだけですが、なんとなく不満でした。それだけです。

Angularは、https://github.com/kriskowal/qをもとにしているとドキュメントに書いてありますが、どうしてメソッド名を変えたのでしょう。きっと、なにか理由があるのだとは思いますが、読み取れなかったです。

タイトルにある「困った」はこの事実にしばらく気づかず、コーディングではまってしまった、ということでした。


2/16追記。

q.jsのソースコードを読んでいたら、catch,finallyも一応サポートされていました。いずれにしても常に、thenだけを使っておくほうが安心な気がします。

オフラインファーストという言葉があるようです。言葉の存在こそ知りませんでしたが、以前からオフラインアプリを推進したいと思っていました。モバイルファーストなら、HTML5アプリといってもオフラインで使えるべきでしょう、と思います。これには賛否両論あるみたいですが。

http://blog.joelambert.co.uk/2012/11/26/offline-first-a-better-html5-user-experience/

2012年にこんな記事があったのですね。

さて、このオフラインファーストの文脈で、最近注目しているライブラリがあります。Breezeです。 まさにオフラインアプリを作るためのクライアントサイドデータ処理ライブラリです。

stackoverflowでBreezeの代替はないのかという質問がありましたが、

http://stackoverflow.com/questions/15938866/alternative-to-breeze-js

今のところ、ぱっとしたものはなさそうです。今後の登場にも期待しましょう。

Breezeはだいぶ強力なようですが、それを理解するのはなかなか骨が折れます。基本的なデータベースの概念が理解できていないとつらいのかしら。ドキュメントもあるのですが、どこから読んでいいのか分かりにくいです。stackoverflowで聞いてね、って感じです。それはそれで、いいアプローチだとは思います。 ちなみに、Angularのドキュメントは読みやすいと感じます。

今日の本題。MEANスタックって知っていますか? ちらほら記事があるので、知名度はそこそこでしょうか。 MongoDB, Express, AngularJS, Node.jsの頭文字をとっているのですが、個人的には順序が気になって仕方がないです。まあ、語呂合わせなんでしょうけど。

http://mean.io/

こんなサイトがあるのですね。

http://jp.blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and

なるほど、MongoDBの人たちが呼び始めたのでしょうか。

さて、本当の本題。BMEANスタックというのをご存知の人は少ないのではないでしょうか。まぁ、Breezeの人が呼んでいるだけだと思いますが、最近の一押しです。

BMEANのBはもちろんBreezeです。BreezeのバックエンドとしてMongoDBを使います。AngularJSも実はオフラインアプリと相性がいいと思っています。ExpressとNode.jsはバックエンドのフレームワークですね。

BMEANを見つけたのは、Breezeのサンプルアプリです。

http://www.breezejs.com/samples/zza

これもまたドキュメントが少なくて、ソースコード読めっていうスタンスなのですが、がんばって読んでみています。そこまで完全なオフラインファーストは意識されていないようで、基本的にはオンラインで動かすことを前提に、一時的にオフラインになっても大丈夫なようにできる(そのようにコーディングすれば)というところでしょうか。

しばらくはBMEANスタックを考えてみようと思います。 そのうち、オフラインファーストに求められるライブラリの形が見えてくるかもしれません。


2/8追記。

Breezeのチュートリアルは面白いです。

http://learn.breezejs.com/

インタラクティブに実行できるので、勉強になりますね。もっと内容が増えてくれたらいいのですが。