「express」と一致するもの

あまりにも特殊な話なので書くか迷ったのですが、記録のために書いておきます。

Ghostはnode.jsベースのブログシステムです。 先日、Ghostをherokuにデプロイする話を書きましたが、 使ってみて一つだけ不便に感じることがありました。

エディタでC-hが効かないのです。

C-hというのは普段Backspaceの代わりに使っているキーバインドで、 Ctrlキーを押しながらhを押します。ないと困る人には困るのです。 ブラウザでも使えるようにしています。しかし、これが困るのはLinuxユーザだけかもしれません。 OSXの場合はCtrlではなくてCommandがありますし、Windowsの場合はそもそもC-hが効かないと思います。

GhostのMarkdownエディターは気に入ったので、なんとかしてC-hを使えるようにしたいところ。 ちなみに、C-hが使えない理由はC-hがGhostのショートカットに割り振られていて、 ヘッダータグの挿入になるからです。 ちょっと調べてみましたが、本家Ghostではすぐには解決しそうにない(あまりに特殊環境か)ので、 forkすることにしました。

https://github.com/dai-shi/Ghost

です。修正したコードはほんの2行です。 C-hとC-bに設定されていたショートカットをC-S-hとC-S-bにしました。つまり、Shiftキーも同時に押した時だけショートカットが有効になるようにしました。

さて、これをghost-on-herokuでも使いたいのですが、一筋縄では行きませんでした。 というのも、gitリポジトリからcloneしてデプロイするには、gruntのタスクを走らせて ビルドする必要があるからです。手動でやるのは簡単ですが、これをnpmでやる必要がありました。

結果的には、ghost-on-herokuのpackage.jsonを次のように修正することで解決できました。

diff --git a/package.json b/package.json
index 3af466a..aec84be 100644
--- a/package.json
+++ b/package.json
@@ -10,14 +10,37 @@
   "private": true,
   "version": "0.6.4",
   "dependencies": {
-    "ghost": "0.6.4",
+    "ghost": "https://github.com/dai-shi/Ghost/archive/0.6.4-for-emacs-keybinding.tar.gz",
     "ghost-s3-storage": "~0.2.1",
     "pg": "latest"
   },
+  "devDependencies": {
+    "bower": "1.4.1",
+    "csscomb": "3.0.4",
+    "grunt": "0.4.5",
+    "grunt-bg-shell": "2.3.1",
+    "grunt-cli": "0.1.13",
+    "grunt-contrib-clean": "0.6.0",
+    "grunt-contrib-compress": "0.13.0",
+    "grunt-contrib-copy": "0.8.0",
+    "grunt-contrib-jshint": "0.11.2",
+    "grunt-contrib-uglify": "0.9.1",
+    "grunt-contrib-watch": "0.6.1",
+    "grunt-docker": "0.0.10",
+    "grunt-express-server": "0.5.1",
+    "grunt-jscs": "1.8.0",
+    "grunt-mocha-cli": "1.13.0",
+    "grunt-mocha-istanbul": "2.4.0",
+    "grunt-shell": "1.1.2",
+    "grunt-update-submodules": "0.4.1",
+    "matchdep": "0.3.0",
+    "top-gh-contribs": "2.0.2"
+  },
   "engines": {
     "node": "~0.10.0"
   },
   "scripts": {
+    "postinstall": "(cd node_modules/ghost/node_modules && ln -s -b ../../grunt-* .) && (cd node_modules/ghost/node_modules/.bin && ln -s -b ../../../.bin/bower) && (cd node_modules/ghost && grunt --force init && grunt prod)",
     "start": "node server.js"
   }
 }

devDependenciesはもう少し削れるかもしれません。 この修正に加えて、heroku configの設定も行いました。

$ heroku config:set NPM_CONFIG_PRODUCTION=false NODE_MODULES_CACHE=false

これで期待通りになりました。

今まで、Chromeアプリにはそれほど興味を持っていなかったのですが、 下記の記事でangular.jsと組み合わせているのを見て興味が湧きました。

https://developer.chrome.com/apps/angular_framework

これは、手軽に作れそうです。 Chromeのディベロッパーモードをオンにすると、 Chromeアプリをパッケージする前にフォルダ指定でアプリを実行できます。 しかし、コードをいじるたびにリロードボタンを押さないといけないです。

これは不便、なんとかして先日作った easy-livereload を使いたいと思って、Chromeアプリをexpressと組み合わせて 開発してみることにしました。

manifest.jsonやbackground.jsはサンプル通りに作って、 jadeはパッケージ化するときにコンパイルするようにしました。 package.jsonのscriptsはこんな感じです。

"build": "mkdir -p build; cp manifest.json public/*.js public/*.css build; jade views/main.jade --out build"

Chromeアプリのパスと合わせるようにexpessのルートを設定します。 ひと手間必要だったのは、httpで外部サイトにアクセスする場合です。 Chromeアプリではmanifest.jsonに書けば制限を外せるのですが、 expressでブラウザで動作させる場合はcross domainで引っかかります。 そこで、express側で中継するようにしました。

app.get(/^\/proxy-(http|https)\/(.*)/, function(req, res) {
  request.get(req.params[0] + '://' + req.params[1], {
    encoding: null,
    qs: req.query
  }, function(error, response, body) {
    if (!error && response.statusCode === 200) {
      res.type(response.headers['content-type']);
      res.send(body);
    } else {
      console.log(error, body);
      res.status(500).send('proxy error');
    }
  });
});

こんな感じです。場合によっては、ヘッダーなども中継する必要があるでしょう。 これをクライアント側から呼び出すときは、例えば次のようにしました。

var serverMode = $location.host() === 'localhost';
$http.get((serverMode ? 'proxy-http/' : 'http://') + 'www.foo.bar/hoge.json')

もっといい書き方があるかもしれませんが、まあとりあえずはいいでしょう。これで開発効率が格段に上がりました。

LiveReloadをご存知でしょうか。Web開発をするときに、ブラウザのリロードを省く機能を提供するものです。 類似のものとしては、Live.jsというのもあります。 また、Meteorでも実装されています。

一言で言うと、websocketでコネクションを維持して、サーバ側のファイル変更を通知して自動でリロードするものです。 CSSの場合はリロードせずに変更を適用することもできます。 livereloadは商用のようですが、そのコアのソースコードはMITライセンスで公開されています。

livereloadはブラウザ拡張を使うことがメインのようですが、javascript版のクライアントもあります。 javascript版だと、mobile safariでも動きます。 livereloadのnode.js用のサーバは、https://github.com/livereload/livereload-serverで公開されていますが、 これはそのまますぐには使うことができません。 平たく言うとプロトコルが実装されているだけで、実際に更新通知する機能は含まれていません。

そこで、簡単に使えるライブラリとしてまとめてみました。特徴をまとめると、

  • livereloadのサーバコードの最新版を使用(現在npmに登録されているlivereload系のモジュールはほとんど旧バージョン)
  • livereloadのクライアントコードの最新版を使用(npm化されていないのでnapaというモジュールで導入)
  • fs.watch()を使ってファイル変更を迅速に監視(node-watchというモジュールを使用)
  • node-devを使うことでサーバ側のファイル変更を監視して、クライアントを自動リロードする機能を追加
  • これらの機能をたった一行を加えるだけで使えるようにパッケージ化

となります。

プロジェクトページは下記です。

https://github.com/dai-shi/easy-livereload

最低限の使い方は、

app.use(require('easy-livereload')())

ですが、細かい設定はオプションを引数に与えることでできます。

本質的には他人が作ったライブラリを結合して簡単に使えるようにしただけですが、 相当便利だと思いますので、ぜひお試しください。

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!

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

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

ですが、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/

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

これまでにPassportでFacebook認証をやったことはありますが、OAuth2.0のプロバイダを提供するためにどんなライブラリがあるのか、というのが今回の話です。

OAuth2.0のクライアントライブラリは多くありますが、プロバイダライブラリはどうでしょうか。思ったよりは多かったです。条件は、express/connectのmiddlewareとして使えることです。

現時点でのStar数順に並べます。

今回は、一つずつコメントして行こうと思います。

まず、oauth2orizeですが、これはだいぶリッチな感じです。toolkitとあるように、色々な機能があります。connect middlewareにもなるようですが、standaloneでも動かすこともできる様子。testやexampleも豊富で信頼感あります。

node-oauth2-providerは歴史がある感じです。機能は多くなく、シンプルです。ストレージもコーディングしないといけませんが、個人的には一番好みのライブラリです。

oauth2_server_nodeは、最近更新されていません。oauth2の仕様もdraft10とのことです。

node-auth2-serverは、まだactive developmentということでドキュメントが追いついていないようです。しかし、ドキュメントを見る限りでは機能も豊富で、シンプルなmiddlewareとしても使えそうなので、stableになれば、oauth2orizeよりはexpressで使いやすそうです。

OAuthも、READMEによるとdraftの仕様を参照しており、その後メンテされていない様子です。

oauth2-expressは、READMEが空です。よく見たらコードも未完成でした。

connect-oauth2も、WIPとなっていて1年ほどコミットがないので、中断したプロジェクトでしょうか。いいパッケージ名なのに惜しい。

node-oauth20-providerは、StarもFolkもゼロというプロジェクトですが、見た感じはだいぶしっかりしています。最近コーディングを始めた様子。コミットログをよく見ると、node-oauth2-providerの存在を知らなかったようですね。node-oauth2-serverと比較すると、redis storageの対応が進んでいますが、全体的には劣る感じがします。また、コミュニティがついていないのが痛いですね。今後、作者の進め方次第では状況は変わることはあるでしょう。

以上、まとめると、現時点では、 シンプルで安定しているけど自分でコーディングしないといけないnode-oauth2-providerか、機能豊富でアクティブなnode-oauth2-serverがよさそうです。

でも、どっちも使ってみたわけではないので、ほんの参考程度です。

Node.jsでオフラインで動くHTML5のWebアプリを作るときに、connect-cache-manifestを使う に書いた2つの不満を解消するために connect-cache-manifest を改良しました。

今回の修正点は2つ。 1つはファイルを除外するオプション、もう一つはファイル名とURLのパスが同一でない場合に書き換えるオプションです。

説明よりコードを読んだほうが分かりやすいと思ます。

var express = require('express');
var cacheManifest = require('connect-cache-manifest');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(cacheManifest({
  manifestPath: '/application.manifest',
  files: [{
    dir: __dirname + '/public',
    prefix: '/static/',
    ignore: function(x) {
      return (/(?:\.swp|~)$/).test(x);
    }
  }, {
    dir: __dirname + '/views',
    prefix: '/html/',
    ignore: function(x) {
      return (/(?:\.swp|~)$/).test(x);
    },
    replace: function(x) {
      return x.replace(/\.jade$/, '.html');
    }
  }],
  networks: ['*'],
  fallbacks: []
}));
app.use('/static', express.static(path.join(__dirname, 'public')));
app.get('/html/:name', function(req, res) {
  res.render(req.params.name);
});

このように、ignoreとreplaceがオプション指定できるようになりました。

これで、バックアップファイルは除外されるし、jadeファイルが増えてもリストアップする必要がありません。とても便利です。