読者です 読者をやめる 読者になる 読者になる

Middleman で作った web サイトを Travis + GitHub pages でお手軽に運用する

先日 Middleman を使って sapporojs.org をリニューアルしました。 その際に得られた Middleman での web サイト運用の知見をご紹介します。

Middleman って??

簡単に説明すると、静的 web サイトジェネレータです。 Jekyll をご存知の方であれば、似たようなものをイメージしていただけるとよさそうです。 Middleman の方が優れている点は、 Asset PipelineTemplate Helpers などの便利な機能を利用をすることができることです。

逆に Jekyll の方が有利な点としては、 GitHub pages が使えるためデプロイが楽であるという点があります。

そこで今回は、 Jekyll と同じく Middleman を GitHub pages に簡単にデプロイする方法をご紹介します。

流れ

Middleman のディレクトリ構成はおおまかには以下のようになっています:

.
├── build (生成された html)
└── source (人間が編集するファイル)

Middleman で生成した web サイトを GitHub pages で公開するためには、build ディレクトリを root ディレクトリとして gh-pages ブランチに push する必要があります。

そのため以下のような手順を設定し、デプロイを自動化することにします:

  1. Travis でビルドする
  2. Travis で commit を作成する
  3. Travis から GitHub pages に push する

準備

まずは Middleman プロジェクトの用意しましょう。

お使いのプロジェクトがあればそれを、なければ middleman init したものを利用します。

またデプロイ環境には、GitHub pages を利用するため、GitHub にリポジトリを作成しておきます。 source の管理は master ブランチ、デプロイ用に gh-pages ブランチを利用することとしてご説明します。

1. Travis でビルドする

まずは、Travis の設定をおこないます。 詳しくは本家サイトを参照してください。

手元で .travis.yml を作成します。

---
language: ruby
script: bundle exec middleman build

これで、GitHub に push した際に Travis でビルドが実行されるようになりました。

2. Travis で commit を作成する

Travis だと Git に user.name, user.email の設定がされていないため、そのままだと commit が作れません。 そこで commit を作るために必要な環境変数を設定します。

.travis.yml に以下を追記します。

env:
  global:
    - GIT_COMMITTER_NAME=<名前>
    - GIT_COMMITTER_EMAIL=<Eメール>
    - GIT_AUTHOR_NAME=<名前>
    - GIT_AUTHOR_EMAIL=<Eメール>

(名前、Eメールは適宜設定してください)

では、gh-pages ブランチを clone してきて、 ビルドが成功したあとで commit を重ねるよう設定を行いましょう。

.travis.yml に以下の内容を追記します。

before_script:
  - git clone --quiet <リポジトリのURL> build
  - pushd build
  - git checkout -b gh-pages
  - popd

after_success:
  - cd build
  - git add -A
  - git commit -m 'Update'

(この時、リポジトリURL には ssh プロトコルを利用しないようにしてください。接続先の fingerprint の確認を求められて先に進むことができなくなります)

これで、Travis で git の commit を作成することができるようになりました。

3. Travis から GitHub pages に push する

Travis から GitHub に push するためには一工夫必要です。 というのは、GitHub の認証を Travis から通過する必要があるからです。

このためには、以下で議論されているように、https プロトコルで GitHub の OAuth トークンを利用する というのがシンプルな方法です。

では、GitHub のトークンを取得しましょう。(すでに利用可能なトークンをお持ちであればこの手順はスキップしてください) 以下を参考に、トークンを取得してください:

この時、 scope に repo が必要なので忘れず設定してください。

上記で取得したトークンを Travis に設定しましょう。 この時、.travis.yml に書けば GitHub にはアクセス可能になりますが、public な場所に置くと誰でもトークンの権限を利用可能になってしまうので推奨されません。

そこで、TravisAPI を利用して安全に設定することにします。

(初回は Travis へのログインを求められます)

$ gem install travis
$ travis encrypt -r <リポジトリ名> "GH_TOKEN=<GitHub トークン>"

secure XXX という値が返ってくるので、それをそのまま .travis.yml の global -> env に追記します。

env:
  global:
    - secure: <Travis のセキュアキー>

このトークンを利用して GitHub に push する設定を .travis.yml に追記します。

 after_success:
   - cd build
   - git add -A
   - git commit -m 'Update'
+  - '[ "$TRAVIS_BRANCH" == "master" ] && [ $GH_TOKEN ] && git push --quiet https://$GH_TOKEN@github.com/<リポジトリ名>.git gh-pages 2> /dev/null'

git push する際のオプションに気をつけてください。 ここで --quiet の設定を忘れると、Travis のログに GitHub のトークンが表示されてしまいます。

以上で Travis から GitHub に push する設定は完了です。

これで、 master に push するだけで、Travis 経由で GitHub pages に自動でデプロイされるようになりました。 べんり!!

参考

また、手元からも gh-pages に push したくなるという場合もあるかと思います。 そんな時は Rakefile に処理を切り出すというのはいかがでしょうか?

参考までに、ぼくが利用しているものをご紹介しておきます:

最後に

以上で Middleman で作った web サイトを Travis + GitHub pages でお手軽に運用する方法の紹介は終わりです。

今回ご紹介した方法が、みなさまの Middleman を利用した web サイト運用の一助になれれば幸いです:-)

(2013.08.27 追記) 今まで、本記事の通りに設定を行なうと、Travis のログに GitHub のトークンが残ってしまうという不具合がありました。 現在は手順を修正してあるので、本記事に従って Travis の自動ビルドを設定を行った方は GitHub のトークンを expire していただけるとありがたいです。

連絡をくださった @azu_re さん、ありがとうございます!

クライアントサイド JavaScript のパフォーマンス改善には backburner.js が便利!

DOM 処理や Ajax など、JavaScript が外の世界とやり取りする部分というのは、一般的に待ち時間を多く必要とします。

パフォーマンスを改善しようと思った時に、ロジック部分でコツコツと節約するより、まずコストが高い処理を行わないようにするということで、驚くほどの効果を経験をされたことはありませんか?

今までパフォーマンス測定をされた方であればピンとくる部分があることと思います。

そんな時に役に立つのが、今回ご紹介する backburner.js です。

backburner.js って?

backburner.js とは Ember.js の run loop モジュールから切りだされたとても小さなライブラリで、短時間に集中的に発生するメソッド呼び出しの回数を制限したい場合などに利用することができます。

backburner.js ってどう使うの?

例えば、JavaScript で特定のイベントハンドラ内で Ajax 通信を行う場合を考えてみましょう。

$('.action-area').on('mousemove', function(event) {
    // マウス位置を Ajax で送信
});

この例だと、.action-area 要素の上でマウスが動く度にマウス位置を Ajax で送信することになります。WebSocket でやり取りをしている場合でも無い限り、ここまで頻繁に通信を行うことはサーバにとって余分なコストとなる場合があります。

そこで backburner.js です。 上の例を以下のように書きなおしてみましょう。

var backburner = new Backburner(['send']);

function sendMousePoint() {
    var event = this;
    // マウス位置を Ajax で送信...
};

var wait = 100;

$('.handle-area').on('mousemove', function(event) {
   backburner.debounce(event, sendMousePoint, wait);
});

backburner.debounce を使うことで、引数で指定されたメソッドは、同一の Function であれば一定の時間間隔(wait = 100 ms)の中で 1回しか実行されません。 とてもエコロジーですね。

また、いわゆる MVC フレームワークにおいて、モデルの変更をビューがバインドしていて、かつモデルに頻繁に更新が発生するといった場合についても応用出来ます。

これについては、 backburner.js の README に素晴らしくわかりやすいサンプルコードがあるので、こちらを見てみましょう。

モデルのプロパティをひとつひとつ変更していく、というのはよくありえるシチュエーションだと思います。

上述の例のように backburner.js を使うことで、チラツキやもっさり感の原因になりえる DOM の頻繁な更新を抑制することができます。

参考資料

もっと知りたくなった方は、作者の ebryn さんの発表動画とその資料をみてみると良いかもしれません。

それでは、みなさまも backburner.js のすばらしさをご体感くださいませ!

Ember.js をターミナルから触ってみる

最近 Ember.js にはまっています。 せっかくなので、ブラウザを使わずにターミナルからも ember を触れるようにしてみました。

使い方は簡単です。 まずはインストール。

$ npm install -g term-ember

そして実行します。

$ term-ember

あとは ember に触りたい放題です。

term ember> Ember.VERSION
'1.0.0-rc.3'
term ember> var App = Ember.Application.create();
...

ちなみに、各種ライブラリのバージョンを指定して起動することもできます。

$ term-ember --ember 1.0.0-rc2 --jquery 2.0.0

ちょっとしたメソッドの動作確認などに使うと便利かもしれません。

JavaScript 道場を開催しつつ参加してきました

しっかりコードを書きながら他の人のやり方や考え方を共有することができるイベントに参加したいなぁという思いが高まってきていたので、JavaScript 道場というイベントを開催しつつ参加してきました。

JavaScript 道場って?

JavaScript 道場 - connpass の概要がとてもよくまとまっています。

JavaScript 道場は、JavaScript での実践的なコードについて議論をしながらグループでコードを書くイベントです。
普段 JavaScript を書いていて、なかなか上手にできない部分や、汚くなってしまいがちな部分について、みんなで解決方法を模索します。

JavaScript 道場では、JavaScript のプロフェッショナルを師範としてお呼びして、
彼らにアドバイスをもらいながらコードを書くことで、彼らの持っているノウハウを知ることができる機会をご用意いたします。
ぜひ、みなさまが普段から困っている部分を直接質問してみましょう。

Node.js 側の師範として @badatmath 氏を、ブラウザ側での師範として @hokaccha 氏をお迎えします。

JavaScript 道場は、セッションやハンズオンなどのイベントとは違って、考えながらコードを書くイベントです。
目の前のコードに挑むための工夫を、ぜひみなさんのプロジェクトにも持ち帰ってください。

「JavaScript をもっとキレイに書きたい」、「整理されたコードについてもっと考えたい」そんな悩みを抱えている方はぜひお越しください。

あえてひとことでまとめると、チームでコードを書いてお題に取り組むイベントです。(ざっくり)

開催の理由

「ありがたい話を聞く」系のイベントだと、自分で東京に行けばいろいろな種類のイベントに参加することができるし、自分にはあえて札幌で開催する理由はありません。

もちろん、そういうイベントに参加するのはそれで楽しいんですが、現場との距離感というか「明日から自分の仕事につながるもの」をちゃんと持ち帰れるようなイベントに参加したいなぁという気持ちがずっと自分にはありました。

ただ、今までチームでコードを書くイベントはあまり聞いたことがなかったので、せっかく開催するなら自分が参加したいイベントにしたい、という思いで開催を決意しました。 (ちなみに、今回の JavaScript 道場開催にあたって一番影響を受けたイベントは TDDBC in 札幌 です。)

また、このイベントを通じてぼくが実現したかったことは、「自分の身近で JavaScript についてもっと喋れる場ができること」です。 (こう思ったきっかけは、東京のカンファレンスに行くと、ほかの参加者の方々とわりとコアな話で盛り上がれるので、自分が住んでいる札幌でももっとカジュアルに話ができるといいなぁと思ったことでした。)

そのためには、実際にチームでコードに触れて一緒に考えるという経験が一番必要なのではないかと考えたのです。

内容について

午前の部では、 JavaScript を現場で使われている師範をお呼びして、JavaScript を使った開発の話を聞きました。
そのときの講演資料はこちらです:

そして午後の部では、チームでお題に取り組みました。

お題って?

お題といっても、だいぶざっくりした「お客さんの要望」のような抽象的なものだけを用意して、それに対してどうやってアプローチしていくかは完全にチームに委ねるという形にしました。

そして今回のお題はこちらでした:

このうち、どちらのお題に挑戦するかをチームで決めて、好きな方のお題に取り組んでもらいました。

ここでの時間配分は以下の通りです:

時間 内容
13:30 - 13:40 お題発表
13:40 - 17:30 お題
17:30 - 17:50 チームでの感想戦
17:50 - 19:20 チームごとの成果発表

ただ、決まったコードを書くだけではなく、仕様を決めて実装して、そして最後には自分たちの開発を振り返ることができるよう設計したつもりです。 (つまり、ふつうの開発の短縮版にできるよう意識しました。)

各チームでの成果物へのリンクをこちらにまとめています:

最後に

運営者としても参加者としてもとても楽しかったので、ちゃんと開催できてよかったと思っています。 (もちろん、上手くできなかったところやもっと上手くできそうなところはいっぱいあるので、ちゃんと振り返って次につなげます)

師範としてご協力いただいた @bad_at_math, @hokaccha, 一緒に運営してくれた @volpe_hd28v, @puprl, @hachiilcane, そして、参加してくれた方々に深く感謝します。

そして、「次回」というのはすぐには難しいですが、何らかの形でこれからにつながることを考えていきたいですなぁ。

参照リンク

Node.js の EventEmitter のコードを読んで、速度最適化されたコードについて考える

大都会岡山 Advent Calendar 2012 20日目のエントリーです。
前回の記事は @DAI199 さんによる 中国で流行っていること、感じたこと(大都会岡山 AdventCalender2012 19日目) - tagamidaiki.com でした。

20 日目のエントリーは、大都会岡山で生まれ、大都会岡山のコミュニティの方々を尊敬し、そして札幌で暮らしている(!) @tricknotes が書かせていただきます。

ぼくの記事ではタイトルの通り、速度最適化された js コードについて見ていきたいと思います。

はじめに

「その書き方だと遅いから」「こっちの方が速いよ」なんていう言葉をちょくちょく耳にする機会があります。
でもそれって本当でしょうか?

コードの実行速度は処理系の実装と密に関係していて、パフォーマンス計測なしには語ることができません。
また、そのコードの実行のされ方によっても実行速度は大きく変わってくるでしょう。
(たとえば、実行時のホットスポットがどこになるかというのは、引数やオブジェクトの状態などに左右される場合があります。)

そこで今回は、 Node.jsEventEmitter を読んで、 JavaScript の速度最適化について考えてみることにしましょう。

Node.js を選んだ理由

Node.js はイベント駆動の非同期スタイルを強制していることで知られています。そして、このイベント駆動のインターフェースを提供しているモジュールが EventEmitter です。
Streamhttp.Server など多くの主要なモジュールがこの EventEmitter を継承することで、イベント駆動のインターフェースをユーザに提供しています。
(参考)

ということは、 EventEmitter 内でパフォーマンスの悪い箇所があれば、Node.js 全体のパフォーマンスに大きな影響を与えることになります。

実際に、この EventEmitter の中では多くの速度最適化が施されており、これが Node.js 全体の実行速度を維持するための重要なポイントとなっています。
(イベント名に __proto__ など、特定の名前が利用できないという不具合がありますが、それよりもスピードの方が重要であるという方針とのことです joyent/node#4366)

ではさっそく、この EventEmitter 中からいくつかコードを例に挙げながら、実際に速度を計測して本当に速度最適化されていることを確認していきましょう。

EventEmitterevents.js の中で定義されています。

本エントリーでの対象バージョンは Node.js 0.9.4-pre (9f4c0988) とします。

$ node -v
v0.9.4-pre
$ node
> process.versions
{ http_parser: '1.0',
  node: '0.9.4-pre',
  v8: '3.13.7.4',
  ares: '1.9.0-DEV',
  uv: '0.9',
  zlib: '1.2.3',
  openssl: '1.0.1c' }

最適化コードについて

○ Function#apply より Function#call

Function オブジェクトを、任意のオブジェクトをコンテキストとして実行するためのメソッドとして Function#apply と Function#call があります。
多くの処理系の場合と同じく、Node.js の場合もこの両者の間には大きな速度の差あるようです。

そのため、極力 Function#apply を使わないようなコードになっています。

だいぶ濃い感じになっていますね。
普通に書くと、以下のように1行で済むはずです。

handler.apply(this, Array.prototype.slice.call(arguments, 1));

実際、両者の間にどれくらいの速度差があるのか測定してみましょう。

// Function#apply と Function#call の比較
var count = 10000000;
var now, i;
var obj = {};
var key = 'key';

var fn = function() {};

now = Date.now();
for (i = 0; i < count; i++) {
  fn.apply(this, [1]);
}
console.log('* apply: %d ms', Date.now() - now);

now = Date.now();
for (i = 0; i < count; i++) {
  fn.call(this, 1);
}
console.log('* call: %d ms', Date.now() - now);
* apply: 519 ms
* call: 160 ms

たしかに Function#apply の方が遅いようですね。

○ delete より null

オブジェクトの特定のキーに紐付く値を削除する場合、値だけを削除する方法(null や undefined を代入する方法)とキーごと削除する方法(delete キーワードを使う方法)があります。Object#hasOwnProperty などを利用せず、値の truthy/falsy のチェックだけを行うのであれば、この両者の間で大きな動作の違いはありません。
ただ、Node.js の場合は前者の方が圧倒的に高速に動作します。
そのため、不要になったオブジェクトを破棄する際には、delete を利用せず null を代入するようになっています。

EventEmitter の中では、 EventEmitter#removeListenerEventEmitter#removeAllListeners の中で使われています。

// delete と null を代入の比較
var count = 1000000;
var now, i;
var obj = {};
var key = 'key';

now = Date.now();
for (i = 0; i < count; i++) {
  obj[key] = 1;
  obj[key] = null;
}
console.log('* obj[key] = null: %d ms', Date.now() - now);

now = Date.now();
for (i = 0; i < count; i++) {
  obj[key] = 1;
  delete obj[key];
}
console.log('* delete obj[key]: %d ms', Date.now() - now);
* obj[key] = null: 21 ms
* delete obj[key]: 405 ms

速度が処理系に依存することの参考までに、このベンチマークFireFox 16.0.2 (Max OS X) で実行してみました。
すると、以下のような結果となりました。

* obj[key] = null: 5520 ms
* delete obj[key]: 5857 ms

6%程度の差なのでそこまで大きな差ではありません。
この結果から、速度は処理系に依存していることがわかります。

○ Array#slice より for 文 & 代入

配列(もしくは arguments) を部分的に切り出したい場合、Array#slice を利用するのが簡単です。
ただ、Node.js では Array#slice を呼ばないように、 Array を要素数指定で初期化して、ひとつづつ値を詰めている箇所があります。

ここを簡単に記述するなら以下のようになります。

Array.prototype.slice.call(arguments, 1);

これについてはどの程度パフォーマンスに影響が出ているのでしょうか?

// Array#slice(1) と new Array() + for についての比較
var count = 10000000;
var now, i;
var obj = {};
var key = 'key';

var array = ['type', 1, 2, 3 /* any arguments */];

now = Date.now();
for (i = 0; i < count; i++) {
  array.slice(1);
}
console.log('* Array#slice(1): %d ms', Date.now() - now);

now = Date.now();
for (i = 0; i < count; i++) {
  var l = array.length;
  var _array = new Array();
  for (var j = 1; j < l; j++) {
    _array[j - 1] = array[j];
  }
}
console.log('* new Array(): %d ms', Date.now() - now);

now = Date.now();
for (i = 0; i < count; i++) {
  var l = array.length;
  var _array = new Array(l - 1);
  for (var j = 1; j < l; j++) {
    _array[j - 1] = array[j];
  }
}
console.log('* new Array(length): %d ms', Date.now() - now);
* Array#slice(1): 837 ms
* new Array(): 388 ms
* new Array(length): 267 ms

以上のことより、Array#slice を使った場合とそうでない場合では、速度差があることがわかります。

補足

ここまででは書いていませんでしたが、部分的にコードを取り出しただけのベンチマークでは不十分なケースもあります。

コードの書き方だけではなく、文脈に沿って最適化が行われる場合があるので、ちゃんとプロダクションコードに対してベンチーマークをとって全体で動かしてみるまで、速度最適化されたかどうかは判定できません。
(ということを @koichik さんに教えてもらいました。勉強になります!)


結論

ここまで見てきてみなさまお分かりでしょうが、速度に最適化することと人間に最適化することが共存できるとは限りません。速度に最適化し過ぎてリーダブルではないコードになってしまうと、メンテナンス性を著しく下げてしまうことでしょう。しかし、ここまでで見てきた EventEmitter の例のように、可読性を捨ててでも速度に最適化すべき場面というのも確かに存在します。
もちろんその場合には、プロジェクトの性質によってどこまで速度最適化をするかということを判断する必要があると思います。

また、本エントリーの内容は、Node.js 0.9.4-pre (9f4c0988) での挙動です。
今後、同じコードで同じ最適化の効果が得られると保証されているわけではありません。
(関連)

みなさまのアプリケーションでも、もしパフォーマンスが問題であると感じたときは、ホットスポットがどこにあるか計測しつつ、どこまで可読性を犠牲にするかということを考えながらチューニングしていくことをオススメします。

まとめ

以上でぼくの記事は終わりとさせていただきます。
次回の大都会岡山 Advent Calendar 2012sutorada さんです。
引き続き、大都会岡山 Advent Calendar 2012 をお楽しみください:-)

SaCSS vol.40 で「ブラウザサイド MVC 入門」というタイトルで発表してきました

ブラウザの中での MVC を紹介する際に TODO アプリがよく例にあげられるかと思いますが、今回ぼくの発表ではその TODO アプリの手前までを丁寧に説明しました。
「なんでMVCが必要とされているのか」であったり、「解決する問題領域は何か」といったあたりを詳しく見ていく、という内容です。

概要はこんな感じ: (SaCSS vol.40 より)


ブラウザサイド MVC 入門

画面遷移を行わず、1枚の html 上で対話的に操作するような web アプリケーションを目にする機会が増えてきました。
そんなアプリケーションを開発する際、jQuery 主体だったこれまでの開発スタイルだと画面表示と機能が切り離せなくなってしまい、変更に弱い作りになってしまうことになるでしょう。
この問題に対処するにはどうすればよいでしょう?
そんなときは先人の知恵を参考に、その解決策を探してみるのはいかがでしょうか。
ユーザからのインタラクションを受け付けるアプリケーションを上手く構築していく際のやり方として、MVC パターンが広く知られています。
本セッションでは、ブラウザ上での JavaScriptMVC の原点を辿り、その実装のひとつである Backbone.js を例にとって、整理されたコードについて考えてみたいと思います。

ウェブデザイナーさんが多かったのですが、だいぶ置いてけぼりにしてしまった感があって大変申し訳なかったなぁ、というのが反省:-<


今回、声をかけてくれた @h2ham に感謝!

岡山Ruby会議01に参加してきました

岡山Ruby会議01に参加してきました。


Ruby の世界に入ってみよう」というテーマで、これからRubyを始めるひとへのメッセージが詰まった会議でした。


とは言っても、初心者向けな内容ではなく、Ruby を使っている中での実体験の話や、開発自体の話が多くて、とても面白かったです。


そして、ぼくも LT で話しさせていただきました


capybara を紹介する体で、ちょっと変わった使い方も試してみました。
rails 以外のwebアプリケーションにスペックを書いたり、RSpec で実行した結果を画面キャプチャ付きで Excel ファイルに出力したり、という内容です。


最後に、スタッフのみなさま、ありがとうございました&お疲れさまでした。
次回の岡山Ruby会議もまた参加したいなぁ。