タグ「express」が付けられているもの

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')())

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

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

当然のことながら普段コーディングはPCで行っています。 ところが、電車の中などちょっとした隙間時間にコーディングができるといいなと思うことがあります。 ちょっとアイデアを思いついた時や、バグに気づいて修正したいときなどがしばしばあります。 もしかしたら、ゼロからプロジェクトを作りたいこともあるかもしれません。

iPhoneアプリで、GitHubのクライアントはいくつかあります。 そのうちのいくつかはエディタ機能を備えているものもあります。 しかし、試した範囲ではとても使いたくなるようなエディタではありませんでした。 やはり、iPhoneで楽しくコーディングしたいものです。 iPhoneのような小さい画面でも楽しくコーディングできるようなインタフェースはありえるのではないでしょうか。

無いものは自分で作ろうの精神で作ってみました。 今のところ、GitHub限定です。 色々なライブラリを組み合わせて、Webアプリとして作りました。 特に、ace editorの恩恵が大きいです。 aceは、基本的にはiPhone/iPadでも動くようになっています。 それをさらに使いやすくするため、表示領域を大きくしたり(パフォーマンスの問題は残るのですが) 「コマンドモード」を導入して、vimのようにカーソル移動ができるようにしました。 カーソルキー問題は、Androidではないでしょうから、iPhone特有の問題かもしれません。

作ったWebアプリの名称は、CodeOnMobileです。 mobile codingにしてしまうと、モバイル向けアプリのコーディングの意味になることが多いので、 coding on mobile (devices)から命名しました。

Webアプリは、

http://codeonmobile.axlight.com/

からアクセスしてください。

その前に、GitHubのプロジェクトページ

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

を見てみるとよいでしょう。簡単なスクリーンキャストを載せてあります。 ちょっと長いですが、辛抱強く見ていただけるとうれしいです。

それでは、モバイルでのコーディングを楽しみましょう。

mongodbのaggregateがパワフルで便利

  • 投稿日:
  • by

mongodbはドキュメントベースのデータベースですが、APIがシンプルでよいです。これまで、find()とcount()で足りていたのですが、ちょっと複雑なことをやろうとするとクライアント側で処理するはめになってしまいました。

mongodbの集計機能にはいくつかの種類があります。

http://docs.mongodb.org/manual/core/aggregation/

によると、Aggregation Pipeline, Map-Reduce, Single Purpose Aggregation Operationsとあります。 最後のSingle Purpose Aggregation Operationsはまさにcount()のようなものなのですが、他にもdistinct()やgroup()もあります。SQLから類推すれば機能は分かりやすいでしょう。 Map-Reduceは並列処理にはいいでしょう。ただ、JavaScriptの関数を記述することになります。なんでもできるといえばなんでもできるのですが。 残るは、Aggregation Pipelineです。これは処理を逐次的に書くので分かりやすいのが利点ですが、注目したのはJSONで記述できることです。JavaScriptの関数と違ってシリアライズができるのがうれしい。Pipeline Operatorsというのがあって、そこそこ柔軟に書けます。

今回は詳しく述べませんが、参考までにパイプラインのサンプルを載せておきます。

var pipeline = [{
  $project: {
    record: '$records'
  }
}, {
  $unwind: '$record'
}, {
  $match: {
    'record.name': target_name
  }
}, {
  $group: {
    _id: 'all',
    count: {
      $sum: '$record.score'
    }
  }
}];

この$unwindがいいですね。ドキュメントを展開してくれるのです。

欲を言えば、パイプラインではなく、処理を分岐できる有効グラフを記述できたらさらに強力だったろうに。

さて、なぜJSONで記述したかったかというと、social-cms-backendで使いたかったからです。aggregateをサポートしてバージョンアップしました。

https://www.npmjs.org/package/social-cms-backend

これまでに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がよさそうです。

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

Railsより簡単にSNSを作れるようにすることを目指したライブラリ social-cms-backend を改良しました。

これまでは、認証機構はFacebook連携のみが実装されていたのですが、 Facebookに依存したくないアプリを作るケースも考え、 今回HTTP Digest認証も入れました。 Formによるパスワード認証も一応あったのですが、これは動作確認用で、not for production扱いです。 やはり生のパスワードがネット上を流れるのはよくないということで。 (SSLなしの想定)

Digest認証のサーバ側は passport-http を使いました。 Digest認証をそのまま使うのは不便なので、2つ工夫をしました。 一つは、RememberMeクッキーを併用することで、 もう一つは、JavaScriptでDigestを生成して認証することです。 Digest認証の最大の問題はUIがブラウザ依存だということです。 realmで指定する文字列しか指定できません。 また、ブラウザがパスワードを覚えてしまうので、アプリ側からログアウトできません。 そこで、DigestのヘッダーをJavaScriptで生成しました。 これは、完全な仕様通りではありません。細かい話は省きますが、 今回のサーバ実装ではたまたまnonceのサーバ側のチェックを省略していたのでできてしまったまでです。 ただ、もしチェックではじかれた場合でも通常のブラウザのDigest認証に fallbackするだけなので、リスクが増えるわけではないはずです。 逆に、パスワードを一回目で間違えた場合もfallbackしてしまいます。 このあたり、いい解決法がないようです。 (そもそもDigest認証の仕様に従わず、なんちゃってDigestにしてしまえばいいのかも)

Digest認証のクライアント側として request をunit testで使いました。使おうとしてから気づいたのですが、 Digest認証の実装が不十分だったので、直してPull Requestしておきました。 無事マージされました。

social-cms-backendはangular.jsでTwitterっぽいアプリを作るのに 便利なのですが、ちょっと使い方にくせがあります。 もっとチュートリアルを増やさないと使えるレベルにならないかも。

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ファイルが増えてもリストアップする必要がありません。とても便利です。

以前の記事で紹介した、 connect-cache-manifestですが、 実は自分でちゃんと使ったことがありませんでした。

今回、一通り動くところまで使ってみたので、参考までに紹介します。 Expressと Jadeを使う前提です。

多くの場合、ディレクトリの構造は次のようになるのではないでしょうか。

|-- app.js
|-- public
|    |-- js
|    |    |-- foo.js
|    |    |-- bar.js
|    |-- css
|    |    |-- foo.css
|    |    |-- bar.css
|    |-- images
|         |-- foo.png
|         |-- bar.png
|-- views
     |-- xxx.jade
     |-- yyy.jade

このような場合での、connect-cache-manifestの設定は次のようになります。

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/'
  }, {
    file: __dirname + '/views/xxx.jade',
    path: '/html/xxx.html'
  }, {
    file: __dirname + '/views/yyy.jade',
    path: '/html/yyy.html'
  }],
  networks: ['*'],
  fallbacks: []
}));
app.use('/static', express.static(path.join(__dirname, 'public')));
app.get('/html/:name', function(req, res) {
  res.render(req.params.name);
});

これで、/application.manifestが生成されるようになります。 使ってみて不満が二つありました。一つは、emacsやvimで*~のバックアップファイルができた場合にそれもマニフェストに入ってしまうこと、もう一つは、jadeファイルが複数ある場合にリストアップしなければならないことです。そのうち気が向いたら改良しようかな。

これで、HTML5のマニフェストを使いつつ、ほとんど気にせずに普通にコーディングができます。

そうそう、ついでにもう一つ。ファイルを新規に追加したときは、nodeを再起動しないといけません。これは、node-supervisorやnodemonを使えば解決できるかもしれません。試していませんが。

みなさん、AngularJS使ってますか?

Angularを使うと、お手軽にWebアプリ(と言っていいのかな)が作れます。

そのお手軽さをさらに助けるのが、Node.jsとそのライブラリ達です。(いや、Nodeに限った話でもないですが。) 細かい説明は他にお任せしますが、Connect/Expressやその様々なmiddlewareを使うと機能を色々追加できます。 今回、そのmiddlewareの一つとして、SNSのバックエンドライブラリを作りました。 これを使うと、フロントエンドを作るだけで簡単にSNSのサイトができあがります。

試しに、Twitterクローンを作ってみました。今回はフォロー機能などなしの単機能版です。ちなみに、フォローやグループの機能はライブラリには入ってます。

Ruby on Railsのまねをして15分でコーディングするスクリーンキャストを作りました。NodeやAngularの知識を前提としているので、見るだけそれらが理解できるようになるわけではありませんが、どうぞご覧ください。音もテロップもなし、それどころかマウスポインタもなしです。シンプルに。

スクリーンキャストを別ウインドウで開く

コーディングした結果のソースコードはこちらに置いてあります。

https://github.com/dai-shi/twitter-clone-sample/tree/20130804_recorded

実は一文字だけ打ち間違いがあってあとから修正しました。

せっかくなので作ったTwitterクローンを動くようにアップしました。

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

もしよろしければ試してみてください。

AngularJSがnode.jsと通信するときは、RESTでJSONを返すのが便利です。標準的なやり方にしておくと、コードを書く量が減らせます。

node.jsとexpress.jsでRESTを書くときは、

res.json({foo: 'hoge', bar: 123});

のようにします。

これまで、真偽値を返したい場合は、

res.json(true);

としていました。node.jsはこれで正しいです。しかし、これだとAngular的にうまくありません。

Angularはプリミティブな値が返ってくることは想定していないようで、オブジェクトかオブジェクトの配列の形で返してあげる必要があります。文字列の配列もダメです。

今回の例では、

res.json({result: true});

とすることで解決できました。

実は、この話は半年くらい前にも気づいて調べたことがあったのですが、すっかり忘れていました。今日も全く同じことをして1時間ほどはまりました。忘れないように、ということでメモでした。

connect-prerendererのgooglebot対応

  • 投稿日:
  • by

connect-prerendererのStart数が20に到達。すごいのかすごくないのかよく分かりませんが。

今回、Issueが報告されたので、対応しました。

https://github.com/dai-shi/connect-prerenderer/issues/6

AJAXページをクロールするための、Googleの仕様があるらしいです。

https://developers.google.com/webmasters/ajax-crawling/docs/getting-started

仕様は簡単で、hashのすぐあとにエクスクラメーションマークをつけておくと、_escaped_fragment_というクエリパラメータをつけてクロールしてくれるということです。

つまり、

http://aaa.bbb/ccc#!ddd

が

http://aaa.bbb/ccc?_escaped_fragment_=ddd

に変換されて、googlebotからアクセスされることになります。

これは、connect-prerendererの出番です。この変換スキームに対応するように修正しました。詳しくはソースコードを参照することになりますが、使い方は下記のようになります。

var prerenderer = require('connect-prerenderer');
app.use(prerenderer({
  targetGenerator: 'googlebot'
});

この使い方は、まだドキュメントに書いていません。テストができていないからです。

興味ある人いませんか~?

Node.jsで困るのはある機能を満たすためにどのパッケージを使っていいか分からないことです。発展途上ということで納得しましょう。発展途上というか生態系。

今回、Facebook認証をするためのモジュールを探しました。 stackoverflowで色々比較コメントがあり、それらやREADMEを参考にしました。

connect-authというのは名前もいいし、それなりのStar数もあります。シンプルでよさそうなのですが、よく使い方が分かりません。人気があるのは後発のeveryauthのようです。しかし、ドキュメントを読んでみてもどうもピンときません。なにか、感性が合わないような気がしました。

そこで、Passportを試すことにしました。 http://passportjs.org/guide/facebook/にドキュメントがあります。everyauthと比較するとコーディング量は多いかもしれませんが、なんとなくこちらの方が合う気がします。それでも、accessTokenが欲しいだけの場合は、userオブジェクトなんて作らなくてもいいのですけど。これはeveryauthも同じ(?)なので我慢するとします。

簡単に今使ってみた方法を紹介します。

まずは、ライブラリのロードです。サンプルをコピペしただけです。

var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;

続いて、FacebookStrategyの設定です。accessTokenだけ欲しかったので、それをuserオブジェクトにします。必要であれば、profileとかも入れればよいでしょう。userオブジェクトは、req.userで参照できるので、accessTokenはreq.user.accessTokenになります。

passport.use(new FacebookStrategy({
  clientID: process.env.FACEBOOK_APP_ID,
  clientSecret: process.env.FACEBOOK_SECRET,
  callbackURL: process.env.CALLBACK_URL
}, function(accessToken, refreshToken, profile, done) {
  done(null, {
    accessToken: accessToken
  });
}));

サンプルにあったシリアライザも入れておきます。これは書かなくてもよさそうと思ったのですが、ソースみてもデフォルトがあるように見えず、念のためいれておきます。

passport.serializeUser(function(user, done) {
  done(null, user);
});
passport.deserializeUser(function(obj, done) {
  done(null, obj);
});

強制的にログインさせるmiddlewareです。一度、ログインを促すページを表示するほうが親切かもしれません。

function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    next();
  } else if (req.url.lastIndexOf('/auth/', 0) >= 0) {
    next();
  } else {
    res.redirect('/auth/facebook');
  }
}

expressの設定です。セッションを使うのでその設定が必要です。

var app = express();
app.use(express.cookieParser());
app.use(express.session({ secret: 'foobar' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(ensureAuthenticated);

最後に、expressのルートの設定をします。failureRedirectは暫定です。

app.get('/auth/facebook', passport.authenticate('facebook'));
app.get('/auth/facebook/callback', passport.authenticate('facebook', {
  successRedirect: '/',
  failureRedirect: '/auth/loginfailed'
}));
app.get('/auth/loginfailed', function(req, res) {
  res.send('login failed');
});

以上、こんな感じで使うようです。/auth/facebookのようなルートを設定しなければならないのを面倒と考えるか、分かりやすいと考えるかが、Passportを受け入れられるかの境目かもしれません。

最近、connect-prerendererのIssueを報告してもらって、少しずつ不具合が見つかっては修正しています。

うまく解決したのは、

の二つ。前者は、

<div>
  count={{count}}
  <span>hoge</span>
</div>

のように、TextNodeとHTMLElementが並んでいるときに、 ng-bind-templateで上書きしてしまう問題でした。

<div>
  <span>count={{count}}</span>
  <span>hoge</span>
</div>

とコンパイル時になるように、Angular.jsをいじりました。 ただ、CSSがそれを想定しない書き方だと、表示に不具合がでるかもしれません。

後者は、doctypeがjsdomで消えている問題でした。こっちはすぐに直りました。

https://github.com/dai-shi/connect-prerenderer/issues/3 がなかなか手ごわく、まだ解決の糸口が見つかりません。 以前書いた、jsdomからAngularJSの不具合二つ目の問題です。

なんにしても、興味を持ってくれる人が増えるのはうれしいことです。 connect-prerendererはほとんど宣伝していないのに、Star数が増えてきました。それはそれで不思議。

昨日、見つけてissueの登録をしておいた、connect-offlineの件です。

https://github.com/dustMason/connect-offline/issues/1

早速返事が来ました、Pull Requestにして欲しいと。仕方ないやるかな、と思ってみたものの、やっぱりCoffeeScriptでは書く気になれません。また、パッケージ名も分かりにくいのではないのかと考え、新しく作ることにしました。

初めは、完全互換のパッケージにしようと考えていたのですが、process.cwd()で相対パスを使っているのが気に入らず、__dirnameを使うようにしたかったので、パスの指定の互換性がなくなってしまいました。

作っているうちに他の改善案(ディレクトリの再帰探索)も思いつき、実装方法もだいぶ変わってしまったので、互換の方向性はやめました。とは言っても、基本的には同じように使えるはずです。

https://github.com/dai-shi/connect-cache-manifest

から参照できます。npmにも登録済みです。

express.jsでHTML5のキャッシュマニフェストを使おう考えています。どうせならconnectのmiddlewareにしたら便利だろうと思って調べました。

GitHubでmanifestをキーワードに色々検索したのですが、見つかりませんでした。connect-manifestという空のプロジェクトがあったくらい。

あきらめて、自分で作ろうかと思ったところで、npmで検索してみました。結果、見つけました。

https://github.com/dustMason/connect-offline

offlineという名前だから、GitHubの検索では見つからなかったようです。 Node.jsはパッケージ探しが難しいですね。

このconnect-offlineはStar数が4しかありません。あまり、キャッシュマニフェストをmiddlewareで欲しいと思う人はいないのでしょうか。それとも、名前が悪くてみんな見つけられないのでしょうか。

READMEを読むと、以前のconnectにはcacheManifestというmiddlewareがバンドルされていたようです。connectのリポジトリを探りましたが、確かに、version 1.0より前のタグには存在します。なぜ、やめたのかは分かりませんでした。(消えたファイルのgit logを見ればいいのも)

さて、connect-offlineはちょっと想像していた機能が足りなかったので、自分で修正しようと思ったのですが、ソースがCoffeeScriptだったので手を出しませんでした。(CoffeeScriptはなぜか好きになれないので)

代わりに、issueにしておきました。

https://github.com/dustMason/connect-offline/issues/1

connect-prerendererが一応完成

  • 投稿日:
  • by

AngularJSのマイルストーンにserver-side prerenderingというのがあるのですが、当分できそうにないので自分で作ってしまっています。

https://github.com/dai-shi/connect-prerenderer

express.jsのmiddlewareで使うことを想定していますが、express.jsには依存しないようにしたので、connect-prerendererです。expressとconnectの違いは、他のブログなどでも説明されていることでしょう。

何をやっているかを一言で言うと、サーバ側でjsdomを使ってJavaScriptを走らせて、レンダリング後のHTMLページをクライアントに返すというものです。

基本的なところはしばらく前にできていて、簡単なテストケースは動いていたのですが、AngularJSを動かすのがなかなか大変でした。結局、angular.jsのソースに手を入れることになりました。

AngularJSを使った簡単なテストケースも動いて、満足です。 もってきてすぐ使えるようなライブラリではないですが、たぶん世界初の取り組みでしょう。

あとははAngulaJS側でもっとうまく取り込んでもらわないとつらいところです。今は決めうち(ng-repeat="..."はOKだが、class="ng-repeat:..."は動かないとか)でやっている部分があるため。

あまり説明できずに意味不明かと思いますが、これにて。