「GitHub」と一致するもの

みなさん、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/

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

ttyrec/jsttyplayがうまくいったので、ちょっと欲が出てきて、GitHubのプロジェクトのREADMEに作ったスクリーンキャストを載せたくなりました。

Markdown自体はHTMLタグを埋め込むことができるのですが、README.mdが表示されるときには全てのタグが使える訳ではありません。

ざっと試したところ、script,canvas,iframeは消えました。たぶん、embedも消えるでしょう。

最後の手段は、画像です。画像は載っているのを見たことがあります。 そこで、スクリーンキャストをGIFアニメーションに変換しようと思いました。

ttyrecは文字情報が記録されているので、そこから直接GIFアニメーションに変換できたら効率的な(サイズの小さい)ファイルが作れるはずだ、と期待したのですが、残念ながらそのようなツールは見つかりませんでした。

仕方ないのでテキストベースはあきらめ、普通に画面キャプチャすることでスクリーンキャストする方式に戻ることにしました。 GIFアニメーションを出力できるスクリーンキャストツールとしては、 Screencast-O-Maticがお手軽そうだったので試してみました。

まず、初めに普通にターミナルを録画して変換してみましたが、GIFファイルが大きくなりすぎました。そこで、フォントを小さくして、さらにttyrecで記録したものを倍速で再生することで時間も半分にしたところ、だいぶ小さくはなりました。それでも時間が長いと大きいですが。

これを、README.mdに埋め込むのですが、ファイルはどこに置くかというと、 GitHub pagesに置きました。

ところが、まだファイルが大きすぎるのかうまくいきません。Not Foundになってしまいました。仕方ないのでraw.github.comを使ってファイルの位置を指定したら、今度はうまく行きました。ちなみに、サイズが大きすぎるとblob is too bigというエラーがでます。

本来はraw.github.comを使ってコンテンツ配信をするのはよろしくないのだと思います。ファイルサイズをもう少し小さくすることができたら、GitHub pagesのリンクも再挑戦することにします。

最近、ブログネタがどんどんマイナー路線に行ってしまっていますが、だれか読者はいるでしょうか。


8/2追記。

4倍速にして時間を圧縮し、fpsを半分にして、ファイルサイズを小さくしたところ、GitHub pagesのリンクでも動くようになりました。 これにより、GitHub pagesを開く際はそのままリンクが使われ、github.comを開く際は、よろしくCDNにコピーされるようになりました。

動画は便利ですね。身体的な技を習得する場合もそうですが、最近ではプログラミングも動画で習得するようですね。自分は使っていませんが、ドットインストールは便利そうです。

さて、本題です。スクリーンキャストをテキストベースで行いたいと思いました。ターミナルだけで手軽にやろうと。

ttyrecというツールを使いました。Ubuntuではaptでインストールできます。

$ apt-get install ttyrec

実行すると新しいシェルが始まるのでそこで一通り作業してexitすると、全て記録されています。とても簡単です。

ttyplayで実行できます。再生中に速度も変えられるので便利。2倍速くらいでもついていけます。

ttyrecで録画したスクリーンキャストをwebにアップする方法を調べました。2つ見つけました。

  • playterm.orgというサイトにアップする
  • jsttyplayというライブラリを使う

初めはお手軽なplayterm.orgを使うつもりでしたが、一つだけ難点が。試してないから確かではないのですが、一度アップすると削除ができない様子なのです。もうちょっと自分で制御したくて、jsttyplayを使うことにしました。しかし、jsttyplayはライブラリなのでいろいろ手を入れないと使えません。まずは、Webサイトが必要です。

今回は、GitHub pagesを使いました。gh-pagesブランチにjsttyplayをコピーして、test.htmlを参考にHTMLを書きました。不具合もいくつかあって、

  • Rewindボタンが動かない(バグ修正した)
  • PlayPauseボタンの動作が不明(非表示にした)
  • fontが相対パスなので、ディレクトリが異なると読み込めず
  • ttyrecordファイルのURLがハードコードされている

などが気になりました。

あと、注意点がいくつかあります。

  • ターミナルのサイズを記録時と再生時で同じにする必要がある。これは、ttyrecの制限。playterm.orgでは80x24を標準としている。
  • TERM=xtermでは途中で固まった。TERM=vt100だと大丈夫。ttyplayは問題ないので、これはjsttyplayの制限。

これらをクリアして見事ttyrecで記録したスクリーンキャストをWebに載せることができました。HTML5 Canvasで動いているようです。ターミナルをエミュレートしている感じでしょうか。かっこいいですね。

jsttyplay_screenshot.png

このシンプルさがいいです。 再生速度変更ボタンはありませんが、簡単に作れそうです。 そのうち暇ができたらちょっと改造してみようかと思いました。

興味がある方はお試しください。

jsdomへのpull request: XHRサポート

  • 投稿日:
  • by

connect-prerendererをちゃんと動かすためにやったjsdomの修正その2をpull requestにしました。

https://github.com/tmpvar/jsdom/pull/654

実はXHR自体は、 node-XMLHttpRequest をそのまま使うだけでほとんど苦労はありませんでした。

大変だったのは、クッキーを引き継ぐところでした。 そもそもXHRを使わない場合も、jsdomはクッキーの引継ぎをサポートしていませんでした。XHRを使わない場合というのは、JavaScriptやCSSや画像ファイルなどを読み込む場合です。

まず、JavaScriptのロード時にもクッキーを引き継ぐようにするコーディングをしてから、XHRでもクッキーを引き継ぐようにしました。XHRはnode-XMLHttpRequestの内部のコードまで理解しなくてはならず、苦労しました。ちょっと強引に実装したため、もしかしたら将来のバージョンのnode-XMLHttpRequestでは動かないかもしれません。

pull requestは無事マージされて、jsdom v0.8.1がリリースされました。興味がある方はお試しください。

https://npmjs.org/package/jsdom

connect-prerenderer もこのバージョンを使うように修正しました。

https://npmjs.org/package/connect-prerenderer

今回は、npm周りのTIPSです。特に新しい知見ではないのですが、メモのために書いておきます。

node.jsのパッケージマネージャであるnpmは依存パッケージの解決をしてくれます。依存パッケージは、package.jsonに書いておきます。

例えば、expressを使う場合、

"dependencies": {
  "express": "*"
}

のように書きます。バージョン番号を指定しておきたい場合もあります。

"dependencies": {
  "express": "3.1.0"
}

のように書きます。他にも不等号やチルダを使ってバージョン番号の範囲を書くこともできます。今回はそのあたりは詳しく書きませんが、package.jsonにはバージョン番号の範囲を書いておくことがおすすめです。node.js関連のパッケージはまだ発展途上で仕様が変わることがよくあるからです。そうでなくても、">=3.0.0"などと書いておけば、"3.0.0"では動いたのだということが分かってよいかもしれません。

さて、本題です。依存パッケージがnpmに登録されていない場合はどうすればよいでしょう? 一つの答えは、登録してしまえばよいのです。が、登録したくない場合もあります。今回そのようなケースがありました。それは、GitHubでforkした場合です。将来的にはPull Requestして取り込んでもらうつもりなら、npmに独自バージョンを登録するのはうまくありません。そこで、npmに登録されていないけれど、依存パッケージとして使いたいときにどうするかという話です。

GitHubを使うとしても方法は2通りあります。一つは、tarballで取得する方法、もう一つは、gitプロトコルで取得する方法です。

前者は、

"dependencies": {
  "jsdom": "https://github.com/dai-shi/jsdom/tarball/3bb5b24c5e"
}

のように書き、後者は、

"dependencies": {
  "jsdom": "git://github.com/dai-shi/jsdom.git#3bb5b24c5e"
}

のように書きます。

どちらがいいのでしょう? よく分かりません。 試しに一回だけ、npm installの時間を計測してみました。 結果、前者が11秒、後者が14秒でした。 gitプロトコルの方がオーバヘッドがあるのかもしれません。 httpプロキシしか使えない場合は、前者に決まりですね。 GitHubがtarballのURLを廃止したら(ZIPのURLはリンクがありますが、tarballってどこまでオフィシャルなのでしょう?)、後者に決まりですね。

今のところはどちらでもよいのかもしれません。


10/20追記。

"https://github.com/dai-shi/jsdom/tarball/3bb5b24c5e"

の代わりに、

"https://github.com/dai-shi/jsdom/archive/3bb5b24c5e.tar.gz"

と書く方法もあるようです。こっちの方がオフィシャルな感じがしますね。

connect-prerendererをちゃんと動かすためにやった 修正をjsdomのpull requestにして欲しいと依頼されました。

unit testを書いて欲しいということなので、書いてみたのですが動かなくて苦労しました。 __defineGetter__と__defineSetter__を初めて使いました。 ECMAScriptではこれらは入っていない(definePropertyを使う)そうですが、nodeでは__defineGetter__と__defineSetter__は使えるようです。

結局、当初やっていた修正では全くダメ(なぜconnect-prerendererが動いたのか不明)で、window.locationまわりのコードを全部書き直しました。 おかげで、全体的により仕様に合った動きをするようになり、立派なpull requestができあがりました。

ちゃんとマージされたようで、めでたしめでたし。

https://github.com/tmpvar/jsdom/pull/650

ちょっと新しいことに挑戦しようと思って、MongoDBを勉強中です。 node.jsから使うモジュールとして、 native driver と、 mongoose をみました。

Railsに慣れている人はODMのmongooseのが受け入れやすいかもしれませんが、 自分は生のJSONをいじりたかったので、native driverを使うことにしました。

http://christkv.github.com/node-mongodb-native/

を読み始めましたが、初めはとってもとっつきにくかったです。 どこから読んでいいか分からなかったです。

チュートリアルの一つ目から読むのがおすすめです。

全体像を理解してからは、Manualが読みやすかったです。 http://mongodb.github.io/node-mongodb-native/api-generated/collection.html が一例ですが、URLにapi-generatedと有るので、ソースコードから生成しているのでしょうか。ちなみに、ページの最初にあるUsageも読めなくはないですが、内容も古く中途半端な感じがしました。

MongoDBのAPIには満足です。JSONをそのまま保存でき、クエリで検索できます。Collectionを事前に生成しておく必要もありません、オンディマンドで作ってくれます。このあたりを便利に使うと、ODMには戻れないかも。まあ、自由度が高すぎるのでそれを嫌うケースもあるとは思います。

ところで、自分がやろうとしたことでできないことがこれまでに一つありました。

クエリもJSONで書くのですが、例えば、

{"product_name":"apple", "color":"red"}

のように書くと、product_nameがappleで、かつ、colorがredのデータを検索することになります。「または」の指定も可能で、

{"product_name":"apple", "$or":{"color":"red", "name":"ringo"}}

のように書けます。しかし、この書き方だと、 「(AまたはB)かつ(CまたはD)」のようなクエリが書けないのです。 うーん、惜しい。

Issueになっているのかと調べようと思いましたが、Issue Trackerの使い方がよく分かりません。nodeのドライバーの問題ではなく、本体の問題だろうとは思うので本体のIssueを探そうとは試みました。

https://jira.mongodb.org/secure/IssueNavigator.jspa

これで合ってますよね。"or"という検索キーワードが論理式と認識されているのですかね。とりあえず、あきらめることにします。

そのうち実装されるといいですね。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

Googleリーダがなくなるから、それの代替として作ろうと始めましたが、Googleリーダの期限までには完成しそうにありません。

今日は、RSSの記事のリンクを開く機能を作りました。 CanvasではHTMLは表示できません。そこで、Canvasの外側に独立して iframeを使うことで表示しました。 iframeの表示はjQueryを使いました。KineticJSのようなpure JavaScriptを使っていると、DOM操作はちょっと古びたものに感じます。

さて、RSSのリンクを開くためのボタンも作ってみました。 右上の方に矢印のアイコンを置いて、それを押すとiframeが開きます。 その矢印アイコンですが、Kineticのshapeで作成するのは面倒だったので、inkscapeで作成してSVG DataをKineticのpathで読み込みました。

コードはこんな感じです。

var path = new Kinetic.Path({
  x: 0,
  y: -6,
  data: 'M 23.618434,50.171286 41.144607,36.346769 23.23155,22.400304 l 0.0092,8.18132 c 0,0 -7.445838,-1.03921 -11.864782,3.095364 -4.4188829,4.134559 -4.3930939,13.742678 -4.3930939,13.742678 0,0 2.442808,-4.269676 6.9240349,-5.841822 4.481182,-1.572147 9.583341,-0.746586 9.583341,-0.746586 l 0.127874,9.340028 z',
  fill: '#f0f0f0',
  scale: 0.43
});

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/

実行サンプルはこちらです。

http://canvas-rss-reader.herokuapp.com/

未読既読の機能までできたら、とりあえずは使えるようになるかと思うのですが、どうでしょう。

connect-prerendererのIssue #3を解決

  • 投稿日:
  • by

DailyJSに取り上げてもらって、 一気にStar数が増えたconnect-prerendererですが、 Issueが一つ残ってました。

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

jsdomがらみなので、難しいなぁと思っていたのですが、 こっちの方 でも使ってくれそうな様子なので、ちょっとがんばってみました。

ヒントは、Issue #3で報告してくれた、

http://stackoverflow.com/questions/10054071/jsdom-hashchange-event

と、jsdomの古いIssueの

https://github.com/tmpvar/jsdom/issues/433

でした。

そもそも、hashchangeイベントの発火が実装されていないとのことでした。 みようみまねでjsdomに手を加えて、コードを追加しました。

結果、動くようになりました。まだ簡単なe2eテストが動いただけですが、 不具合があれば誰か報告してくれることでしょう。それまで、待つ。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

今回は、フリックで横スライドに挑戦しました。 しばらく時間が空いてしまったのは、どうやって実装するかイメージがわかなかったからです。 試行錯誤した結果、比較的満足いくものになりました。

実装するときに気にしているのは、 普通のスマホアプリと同じくらいの操作性を目指しつつ、KineticJSの機能をフル活用して少ないコーディングで実現することです。 横スライドも、縦スクロールと同様にドラッグ&ドロップの機能を活用しました。 なので、正確にはフリックを判定しているわけではなく、ドロップ後の位置でスライドするかどうか判定しています。 ちょっと使ってみると、直感と異なることもあるのですが、それは今後の課題ということで。

今回、Canvasでアプリを作る時に意識をしているのは、軽快さです。 普通に作ったアプリより軽く感じるWebアプリを作れるのかがポイントです。 現状はまだ機能が少ない(そもそも、まだRSSのURLが開けない)ですが、 自分のスマホではかなり軽快にスライド動作ができるようになっています。

他のスマホではどうなのか気になります。 ぜひ、お持ちのスマホで試してみてください。 下記の実行サンプルをスマホのブラウザで開くだけです。

http://canvas-rss-reader.herokuapp.com/

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/

この時点のコードを参照するには、本日のコミットを参照してください。 上記実行サンプルは、今後最新版に変わっていってしまいますので、ご注意を。


6/23追記。

大切な発見を書くのを忘れていました。TweenのonFinishは呼ばれないことがあってはまりました。Tweenでノードを動かしている時に、ドラッグ&ドロップをすると、終了時にonFinishが呼ばれませんでした。setTimeoutを使うことで解決しました。KineticJSのバージョンは、4.5.2です。この仕様が将来のバージョンでどうなるかは不明です。