「GitHub」と一致するもの

ng-conf 2014っていうイベントが1月にあったようですね。

YouTubeで動画が公開されている(でもすべてではない)ので助かります。とても勉強になりますね。ほんと、いい時代になったものです。

さて、Daniel ZenのTalkを見ました。AngularJSでモバイルアプリを作るために使えるライブラリがいっぱい紹介されていたのでメモしておきます。

PhoneGap

まず、PhoneGap。 今では、Open Sourceなんですね。

http://cordova.apache.org/

使ったことはないですが興味はあります。 一部ネイティブ関数にもアクセスできるようです。

http://en.wikipedia.org/wiki/Phonegap#Supported_platforms

APNも使えるようですね。個人的にはこれが一番大きいかな。

ngTouch

次は、ngTouch。 これは、Angularのモジュールです。タッチイベントを扱えるそうです。 一方、300ms問題だけなら、fastclickでよさそうです。

angular-mobile-nav

続いて、angular-mobile-nav。iOS5くらいのLook&Feelが簡単に作れるライブラリのようですが、READMEによるとangular-1.2だったらもっと簡単にできるということなので、今後使う機会はなさそうです。

angular-gestures

angular-gesturesは、その名の通りジェスチャーを使うためのディレクティブを提供してくれるようです。基本的には、hammer.jsのラッパーなのでしょうか。

angular-jqm

angular-jqmはjQuery Mobileのラッパーディレクティブです。jQueryなんて使わないぜ、と思っていたら、なんとこのライブラリ、jQueryとjQuery MobileのJSには非依存だそうです。つまり、jQuery MobileのCSSを使っているだけ。一気に気に入りました。そのセンスがいいです。機会があれば使ってみたいと思います。

AngularJS Native

これは、PhoneGapのネイティブ関数を呼ぶためのラッパーのようなものでしょうか。それぞれモジュールごとに分かれているようです。

angular-carousel

angular-carouselは、carouselを簡単に実現するためのライブラリです。 carouselってなんて訳すのでしょう?カルーセル? デモを見れば一発で分かると思います。

angular-snap

angular-snapは、snap.jsのラッパーディレクティブです。最近のモバイルアプリでよくある画面全体がスライドしてメニューがでるUIができるようです。snap.jsは依存ライブラリもなく、サポートブラウザも多く、設定も柔軟なようで魅力的です。

Ionic Framework

Ionic Frameworkは、PhoneGap用のフレームワークです。AngularJSも使われているとのことです。このフレームワークの範囲内でできることをやる分には簡単にできそうです。


その他、Tips等も紹介されていますので、興味がある方は直接スライドを見るとよいでしょう。

http://bit.ly/zen-ng-phonegap

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だけを使っておくほうが安心な気がします。

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

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

Gunosy RSS がしばらく前から調子悪く、RSSが取得できていなかったので調べました。

初めの原因は、GunosyページのHTML構造が変わったからでした。 (ところで、何で変わったのでしょう。前バージョンのクリーンで先進的な感じでした。)

HTML構造はすぐに対応できたのですが、大変なのはそれからでした。 なぜか、リクエストがタイムアウトしてしまう現象が発生しました。

https://devcenter.heroku.com/articles/request-timeout

に書いてあるように、herokuは30秒しか待ってくれません。 それを大幅にオーバーしているようなのです。

試しに、30秒ごとに1バイトずつデータを送ってタイムアウトしないようにしてみました。しかし、そのうちメモリがあふれてしまいました。

次に、リクエストを並列で処理しないようにしました。処理中にリクエストが来た場合には503レスポンスを返します。親切にRetry-Afterヘッダもつけました。

これで多少改善されましたが、それでも1つの処理に20秒くらいかかることもあり、30秒を超えることもしばしばでした。

ところで、なぜ、この問題が簡単に解決できなかったかと言うと、 ローカル環境では再現しないからです。ローカルでは数秒で処理が終わるものが、herokuにデプロイした状態では数十秒から場合によっては1分以上かかってました。

これは仕方ないと思い、本番環境でデバッグしました。直るまでは正常に使えないことには変わりないという割り切りで。

で、発見しました。遅さの原因はJSONPathというライブラリでした。これが数十秒もかかってました。ローカルでは数秒。

https://github.com/s3u/JSONPath/issues/14

で報告されているのを発見し、古いバージョンを使うことにしました。 そうしたら、なんと数秒で処理が終わるようになりました。 ローカル環境では、1秒もかかりません。めでたし、めでたし。

しかし、ローカル環境とherokuで10倍から100倍くらいの差があるのは、なぜでしょう。単なるスペックの問題でしょうか。そうであれば納得はしますが、その場合はローカルでも低スペックを再現できるような環境が欲しいですね。

リクエストを並列で走らせないようにする処理は残したままです。完全に取ってしまうと、また不具合があったときに波及が大きいと考えたためです。ただ、せめて数本同時に走らせるようにしないと、503になるケースが多すぎる気もするので、それは今後対応できたらよいかと思います。#2

ところで、Gunosy RSSの不具合は誰も気づいていなかったのでしょうか。 それはそれでいいような、さびしいような。

突然ですが、例えば、githubにリポジトリを置いて、herokuにデプロイするというケースは多いのではないでしょうか? そういうときは大抵、

$ git push master origin
$ git push master heroku

と2つコマンドを打つことになると思います。 それぞれ、git remoteでoriginとherokuが設定されているとして。

これが、一発で済むと楽ではないですか? もちろんローカルでshell scriptを書いてもいいかもしれませんが、 例えば、githubにはPUSH権限があるけど、herokuにはPUSH権限がないような ユーザがいるケースでは、どうでしょう? そういう場合に、githubからherokuに自動でデプロイできたら便利でしょう。

いくつか解決法はあるようですが、CodeShipというのを使ってみました。

http://blog.codeship.io/2013/10/02/how-to-deploy-a-ruby-on-rails-app-from-bitbucket-to-heroku.html

チュートリアルに沿って言われたとおりに設定するだけでできるようになりました。 どうやら、双方にアクセスするための鍵を登録して、 さらにPUSH時のhookを登録することで実現しているみたいです。 鍵の元はCodeShipにあるわけなので、CodeShip側に悪意があればいろいろできちゃいそうですが。

CodeShipはデプロイだけではなく、テストもまわせるなど、使いこなせばいろいろできそうです。

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を使えば解決できるかもしれません。試していませんが。

JavaScriptの正規表現のパフォーマンスに興味があったので調べてみました。 現時点での考察であり、JavaScriptの処理系による違いもありますし、将来的に最適化が進んだ場合は結果が変わることがあると思いますので、ご注意を。

まず、初めに見つけた記事が、これ。

http://stackoverflow.com/questions/9750338/dynamic-vs-inline-regexp-performance-in-javascript

簡単に言うと、

/[a-z]/.exec(str);

と

new RegExp('[a-z]').exec(str);

で、パフォーマンスが違うけど何で?という質問でした。 結論から言うと、これは等価ではありません。 前者は正規表現を一度しか評価しないのに対し、後者は毎回RegExpオブジェクトを作ります。

どうやら、次の3つ関数はほぼ等価のようです。

function func1(str) {
  return /[a-z]/.exec(str);
}

var re2 = /[a-z]/;
function func2(str) {
  return re2.exec(str);
}

var re3 = new RegExp('[a-z]');
function func3(str) {
  return re3.exec(str);
}

「ほぼ」というのは、実際に中身を見たわけではないからです。ただ、処理時間から推測しただけです。

さて、ここまでは理解できていたのですが、実はこの状態だと正規表現のコンパイルは行われていないようなのです。

どういうことかと言うと、

var re4 = new RegExp('[a-z]');
re4.compile();
function func4(str) {
  return re4.exec(str);
}

とすると、処理時間が短くなるケースがあるのです。 これはちょっと意外でした。正規表現のコンパイルというのは最適化の過程で勝手に行われるのかと思っていました。もしかしたら、最適化が行われるのはもっと長い時間処理が回っていないからなのかもしれませんが。

まずは、nodeでテストをしてみました。

https://gist.github.com/dai-shi/7394062

このベンチマークを走らせたら、一つの書き方だけ明らかに速くなりました。それが上記の書き方です。nodeのバージョンをv0.8, v0.10, v0.11と試しましたが、傾向はどれも同じです。

次にjsperfでも試しました。

http://jsperf.com/classname-check-regex-vs-indexof

ちょっと目的が違うのでindexOfとの比較が入っていますが、RegExpの方は大体同じです。 Chromeでの実行結果は、compile()を実行したケースがわずかに速くなりました。試している正規表現が違うので、コンパイルが効きにくいのかもしれません。 Firefoxでの実行結果も、compile()のケースが速くなりました。こちらは10%以上。しかも、全体的にChromeの実行結果より速い。ちょっと意外です。

さて、別のベンチマークも見てみましょう。

http://jsperf.com/javascript-compiled-regex/15

これは、globalマッチのテストケースも入っているのでちょっと分かりにくいですが、それを除けばやっぱりcompile()しているケースが速いです。Chromeで20%ほどアップ。Firefoxで30%ほどアップ。

ちなみに、globalマッチはChromeの場合は最速で、Firefoxの場合はもっとも遅いようです。これは使い方を悩みますね。

これらのベンチマーク結果を踏まえてどういう方針にするかですが、 まず、クライアントサイドのコードの場合は、10%~30%程度速くなることにどれくらい意味があるかですが、ばらつきもあるので、よほどパフォーマンスにシビアでなければ、inline型の正規表現でよいように思います。読みやすいといのが最大のメリットです。つまり、

/[a-z]/.exec(str);

こういう感じです。

一方、サーバサイドのコード、つまり、nodeの場合は、2倍近く速くなるケースもあり、また通常サーバサイドのコードはパフォーマンスを重視することから、現時点ではcompile型の正規表現を使うことに意味があるかもしれません。つまり、

var re = new RegExp('[a-z]');
re.compile();
function func() {
  return re.exec(str);
}

こういう感じです。関数の外で定義しないといけないので、場合によっては気をつける必要があります。変数名の衝突や、意図しない書き換えなど。

var func = (function() {
  var re = new RegExp('[a-z]');
  re.compile();
  return function() {
    return re.exec(str);
  };
})();

のようにする手もありますが、ますます読みにくくなっていきますね。

本来なら、re.compile()がなくてもコンパイルしてくれたらいいように思うのですが、コンパイルそのものにコストがかかるケースもあるので一回しか使わないような場合はコンパイルすべきじゃないということですね。JITなどの最適化はあまり詳しくないのですが、もしかしたら、次第にコンパイルされていくのかもしれません。

と、ここまで書いたところで見つけたのですが、

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features

あれ、compile()は非推奨なのですね。ということは、やっぱり処理系が最適化の過程でコンパイルしていくのが筋ということですね。 非推奨のcompile()を使うとそれを強制的に動かせるのでベンチマークコードの場合は有利といったところでしょうか。

というわけで結論は、よほどの理由がなければ、inline型を使っておけばよいでしょう。new RegExp()を使ってもパフォーマンスが悪くなるわけではないので、場合によっては使ってもよいでしょう。

ぐるっと回って振り出しに戻った感じですが、個人的にはすっきりしました。

JSONの仕様では、Date型は定義されていません。しかし、今回どうしてもsocial-cms-backendでJSONにDate型を埋め込みたくなりました。

stackoverflowで面白い投稿を見つけました。

http://stackoverflow.com/questions/4511705/how-to-parse-json-to-receive-a-date-object-in-javascript

どうやらMicrosoftが.NET Frameworkで独自に拡張しているようです。

http://msdn.microsoft.com/en-us/library/bb412170.aspx

これは、"\/Date(1234567)\/という文字列をDate型とみなすというものです。正確にはタイムゾーンも指定できるようですが、使わないので省略します。

さて、JSON.parseには第二引数にreceiverを指定して独自のdeserializeができるようです。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

stackoverflowの投稿はこれを使ったサンプルです。ただし、receiverに届くときにはバックスラッシュのエスケープは消えているので、"/Date(1234567)/"もDate型に変換されます。分かって使っている分にはよいですが、JSONの仕様からは外れ(いや、Microsoftのやり方もハックですが)、独自のコンベンションという感じですね。

参考までに、コードを載せておきます。

function parseJSON(str) {
  function receiver(key, value) {
    if (typeof value === 'string') {
      var match = /\/Date\((\d+)\)\//.exec(value);
      if (match) {
        return new Date(parseInt(match[1], 10));
      }
    }
    return value;
  }

  return JSON.parse(str, receiver);
}

そうそう、例で示した1234567の数値のところは、Date.now()と同じようにミリ秒です。