CoffeeScript始めたとかBackbone.jsとか

特に書くことが無いけど、何も書いていないと「何か書いてブログ続けないとまずい…」という謎の強迫観念に囚われてしまうので日記を書いてお茶を濁す。

CoffeeScript始めた

CoffeeScriptは人気だけど否定的な評価も色々あったので(「JavaScriptでえーやん」とか「書きやすいけど読みにくい」とか)、半年くらい前に文法を少し見るだけして放置していた。だけど、最近見たViolinっていうJavaScriptのライブラリのサイトにあったCoffeeScriptで書かれてるBackbone.jsアプリのソースコードが思いのほかイケてたので使ってみることにした。

結論からいうと、すごい快適だった。書きやすいしコードがすっきりするし、なによりBackbone.jsとの相性が抜群にいい。調べてみるとやっぱり多くの人がそんな風に感じてるらしい。(参考:「CoffeeScriptは自分にとっては有益だった」, 「はてなブックマーク - CoffeeScriptは自分にとっては有益だった」)

Railsでも標準でCoffeeScriptを使うようになってるし、これからもCoffeeScriptは使い続けていこうと思った。意外とCoffeeScript使ってるJavaScriptライブラリも多いし。

その他雑感

CoffeeScriptの学習はThe Little Book on CoffeeScript(日本語版)を読むのがオススメ。

実際にコードを書く練習するのはRailsでやるのがお手軽でいいんじゃないかと思ってる。というかCakefileの書き方がまだよく分かってない;p

RailsでBackbone.js使い始めた

Backbone.jsが使いやすいのでRailsでも使うことにした。ただ単にvendorフォルダにライブラリ置いといてそこから利用するだけでもよかったけど、探すとやっぱり良さげなBackbone.js用のGemがあったのでそれを使うことにする。(codebrew / backbone-rails) 使い方はこの解説記事が分かりやすい。

解説記事にもあるように、自動生成してくれるコードが綺麗でめちゃくちゃ参考になる。特にrails g backbone:scaffoldは、rails g scaffoldで生成されるおなじみのViewをBackbone.jsで実装したコードを生成してくれるのでオススメ。Backbone.jsでどんなことができるか、どういうふうに使うか、処理の流れ、Routerやtemplateの使い方なんかがよく分かる。

まあもっとも、JavaScript控えめ党としては「アプリケーションの機能の大部分をBackbone.jsを使って実装して、サーバーとのやりとりの大半もJSONで済ませてしまう」っていうのは少し気が引けるので、backbone:scaffoldのコードを直接利用するってことは無いと思うけど。

その他雑感

Backbone.jsについてはこの記事TodoMVCを見ればだいたいどういうものか分かるような気がする。

書籍なら『ステートフルJavaScript』がオススメ。



なんか、もう少し書いておきたかったことがあったような気もするけど、一晩寝たら忘れたのでこのへんでやめておく。最後にまとめるほどでもない内容をまとめて終わりにする。

まとめ

  • Backbone.jsアプリをCoffeeScriptで書くのが(・∀・)イイ!!
  • Backbone.jsはRailsとも相性がいい
  • backbone-railsbackbone:scaffoldで生成されるコードが綺麗で勉強になる

Rails3 で Twitter Bootstrap のパンくずリストを使う

使用環境


twitter-bootstrap-railsにはパンくずリストを生成する方法が標準で用意されている。render_breadcrumbsadd_breadcrumbを使う。

使い方

(twitter-bootstrap-railsプルリクエストのログとかソースとか見たほうが早いかも)

ソース

View側

application.html.erbの適当な場所に

<%= render_breadcrumbs %>

と書いておく。以上。

Controller側


追記: 一部動かないコードがあったので修正しましたm(__)m


パンくずを追加するときにadd_breadcrumb(name, url, option={})を使う。たとえばArticlesControllerがあった場合に、全てのアクションの前に"Articles"というパンくずを置きたければ

class ArticlesController < ApplicationController
  add_breadcrumb "Articles", "/articles"

  def indexendend

というふうにする。


option引数にbefore_filter形式のオプションが書ける。たとえばnewアクションの前にパンくずが置きたければ

class ArticlesController < ApplicationController
  add_breadcrumb "Articles", "/articles"
  add_breadcrumb "new", "/articles/new", :only => [:new]

  def indexend
  
  def newendend

という感じ。


アクション中にもパンくずを置ける。

class ArticlesController < ApplicationControllerdef show
    @article = Article.find(params[:id])
    add_breadcrumb @article.title, @articleendend

無理してbefore_filter風に書かずに、適当なメソッドを定義してそのなかでadd_breadcrumbしたほうがいいかも。ネストしたリソースのコントローラの場合とか。下はArticlesの下に配置されたCommentsControllerの例。

class CommentsController < ApplicationController
  before_filter :set_article
  before_filter :add_breadcrumb_of_articleprivate
    def set_article
      @article = Article.find(params[:article_id])
    end
    
    def add_breadcrumb_of_article
      add_breadcrumb @article.title, @article
    endend

追記: add_breadcrumbsっていうのを作って、まとめて追加するのがスマートかも。

class CommentsController < ApplicationController
  before_filter :set_article
  before_filter :add_breadcrumbsprivate
    def set_article
      @article = Article.find(params[:article_id])
    end
    
    def add_breadcrumbs
      add_breadcrumb 'Articles', articles_path
      add_breadcrumb @article.title, @article
      add_breadcrumb 'Comments', article_comments_path(@article)
    endend


備考

  • add_breadcrumbsで@breadcrumbs配列にパンくずが追加されていく
  • @breadcrumbsの各要素はそれぞれlink_to(name, url, option)という形でlink_toに渡される(optionもlink_toに渡される)
  • render_breadcrumbsの第1引数にパンくず間の区切り文字を指定することもできる(デフォルトは"/")

(追記) Gemfileの設定

twitter-bootstrap-railsのgemの指定はGemfileのassetsグループの中に書くのではなく、Gemfileのトップレベルに書いておく必要がある(twitter-bootstrap-railsのヘルパーメソッドを使うので)。

Markdown文法メモ

Markdownでテキストを整えるという記事に色々と知らないMarkdownの文法があって感心したので備忘録としてメモ。 Markdown文法の全訳も参考に。

追記

どうやらMarkdownの正規の文法ではないものもあるっぽい。たぶん正規の文法はここにあるものだけ。

段落中での改行

段落中で改行したい場合は
行末にスペースを二つ置く

 段落中で改行したい場合は  
 行末にスペースを二つ置く

テーブル

左揃え 中央揃え 右揃え
aaa aaa aaa
bbbbbb bbbbbbb bbbb
c c c


左揃え | 中央揃え | 右揃え
:-----|:-------:|-----:
aaa   | aaa     | aaa 
bbbbbb| bbbbbbb | bbbb
c     | c       | c

脚注

hoge hoge.^訳注
脚注はどこに書いといても勝手に一番下に表示される?^1

hoge hoge.[^訳注]  
脚注はどこに書いといても勝手に一番下に表示される?[^1]

[^訳注]: かくかくしかじか。
[^1]: たぶん。

追記:
はてなブログでは上手くいかない?

参照リンク

直接リンク([テキスト](URL))以外に参照リンクというものもある。 直接リンクで書いた場合より元のMarkdownが見やすい。 以下Markdown文法の全訳の転載…

I get 10 times more traffic from Google than from Yahoo or MSN.

I get 10 times more traffic from Google than from Yahoo or MSN.

直接リンク(`[テキスト](URL)`)以外に参照リンクというものもある。
直接リンクで書いた場合より元のMarkdownが見やすい。
以下[Markdown文法の全訳][]の転載…

  [Markdown文法の全訳]: http://blog.2310.net/archives/6

I get 10 times more traffic from [Google] [1] than from
[Yahoo] [2] or [MSN] [3].

  [1]: http://google.com/        "Google"
  [2]: http://search.yahoo.com/  "Yahoo Search"
  [3]: http://search.msn.com/    "MSN Search"

I get 10 times more traffic from [Google][] than from
[Yahoo][] or [MSN][].

  [google]: http://google.com/        "Google"
  [yahoo]:  http://search.yahoo.com/  "Yahoo Search"
  [msn]:    http://search.msn.com/    "MSN Search"

リンクIDは文字でも数字、スペース、記号が入っていてかまいませんが、英文字の大文字小文字は区別されません。

リンクの定義はMarkdownルールで書かれた文書のどこに位置していてもかまいません。 作者はリンクを設定した段落の直後に記述するのを好みますが、文書の一番最後にまとめて 配置することも可能です。

自動リンク

<>でURLを囲むと自動でリンクにしてくれる。http://google.com/

`<>`でURLを囲むと自動でリンクにしてくれる。<http://google.com/>

以上

AmplifyJSについて

amplify.jsという便利そうなライブラリを知ったのでメモ書き。詳しい情報は公式サイトをどうぞ。

概要

amplify.jsはウェブアプリケーションでよく使う機能をシンプルなAPIにして提供してくれるライブラリのよう。非常に軽量なライブラリで、主なメソッドは5つか6つぐらいだけ。

amplify.jsが提供してくれる機能は大きく分けて3つ。

  • パブリッシュ/サブスクライブパターン(Pub/Sub)
  • クライアントサイドでのデータ永続化操作(Store)
  • Ajax通信の管理(Request)

良いと思った機能は次の辺り。

  • Ajax通信の結果のキャッシングや永続化が簡単にできる
  • Ajax通信の定義が便利。URLの一部分のバラメータ化やキャッシュの設定など
  • APIがシンプルで分かりやすい

依存ライブラリはjQuery(var1.4以上)のみ。ただしJSONモジュールを標準でサポートしていない古いブラウザはstore機能でオブジェクトを保存するのにjson2.jsが必要。

パブリッシュ/サブスクライブパターンの使用(Pub/Sub)

jQueryやBackbone.jsでいうbindtrrigerみたいなのを提供してくれる。

amplify.subscribe()

いわゆるbind。イベントの購読、つまりイベントリスナの登録ができる。

// 公式サイトの説明より http://amplifyjs.com/api/pubsub/
amplify.subscribe( string topic, function callback )
amplify.subscribe( string topic, object context, function callback )
amplify.subscribe( string topic, function callback, number priority )
amplify.subscribe( string topic, object context, function callback, number priority )

(見難くてごめんなさい。あとで直しときます)

topicが購読するイベント名。コンテキストや優先度の指定ができる。コンテキストとコールバック関数の順番がBackbone.jsと逆になっているのが少しアレだけど、優先度が指定できるのはいい。

優先度は値が小さいほうが先にコールバックされる。デフォルトは10。

コールバック関数の戻り値をfalseにすると、それ以降のコールバック関数(つまりそれより優先度の低いコールバック関数)の呼び出しを止めることができる。

amplify.publish()

いわゆるtrigger。イベントの発火ができる。

// 公式サイトの説明より http://amplifyjs.com/api/pubsub/
amplify.publish( string topic, ... )

topicが発火させるイベント名。イベントに渡すパラメータを引数に追加することもできる。

戻り値はtrueかfalse。いずれかのコールバック関数がfalseを返してイベントを中止していればfalseを返す。それ以外はtrueを返す。

amplify.unsubscribe()

イベントの購読をやめさせる。

以上(イベントの購読というかチャンネルの購読ですね、意味論的に言えば)。

クライアントサイドでのデータ永続化操作(Store)

ブラウザの実装に依らないデータ永続化操作を提供してくれる。ブラウザによってlocalStorageやuserData(IE5,6,7)などを使い分けてくれたり、オブジェクトを渡すと勝手にシリアライズしてから保存してくれたり。

amplify.store()

データの保存と取り出し。


amplify.store("foo", { bar: 100 });

保存。


amplify.store("foo");

取り出し。ちゃんと永続化されているのでブラウザをリロードしても取り出せる。


amplify.store();

保存したオブジェクトを全て取り出す。


amplify.store("foo", null);

データの削除。


amplify.store("foo", { bar: 100 }, { expires: 1000 });

保存期限の指定(ミリ秒)。この場合は1秒後にデータを破棄する。


amplify.store.localStorage("foo", 100);
amplify.store.sessionStorage("foo", 100);

保存先を明示的に指定することもできる。

以上。

Ajax通信の管理(Request)

ajaxリクエストを定義しておいたり、JSONPを使ったり、通信結果をキャッシュしたり。とにかく便利そう。

amplifyでajax通信をするにはまずajaxリクエストの定義をする。

amplify.request.define("foo", "ajax", {
  url: "/foo.json",
  dataType: "json",
  type: "GET"
});

"foo"がリクエストの名前(正確にはリソースの名前)。これをamplify.request()で指定するとajax通信ができる。

amplify.request("foo", function(data) {
  console.log(data);
});

通信結果をキャッシュすることもできる。

amplify.request.define("foo", "ajax", {
  url: "/foo.json",
  dataType: "json",
  type: "GET",
  cache: true // ここ
});

こんな感じで、cache: trueとすれば通信結果がメモリ上にキャッシュされて2回目以降のリクエストはキャッシュを取得するだけになる。

cache: 1000というふうに値を数字にすることで保存期限を指定できる(単位はミリ秒)。この例では一秒間だけキャッシュを保持するということになる。

cache: truecache: 1000だとメモリ上にキャッシュしているだけなのでリロードするとキャッシュは消えてしまう。リロードやページ遷移を経てもキャッシュを残したい場合はcache: "persist"を使う。これで通信結果をストレージ上にキャッシュできる。

ストレージ上に保存したキャッシュでも保存期限を指定したい場合はcache:{ type: "persist", expires: 1000 }というふうにする。これでリロードなどで消えずにかつ一定時間後に削除されるキャッシュができる。


urlにパラメータを用いることもできる。下のように{}で囲むことでその部分をパラメータ化できる。

amplify.request.define("foo", "ajax", {
  url: "/users/{user}.json",
  dataType: "json",
  type: "GET",
  cache: {
    type: "persist",
    expires: 1000
  }
});

これで

amplify.request("foo", { user: ore }, function(data) {
  console.log(data);
});

とすれば /users/ore.json へのajaxリクエストを飛ばすことができる。


JSONPによる外部スクリプトとの通信も簡単にできる。例えば

amplify.request.define("github-user", "ajax", {
  url: "https://api.github.com/users/BlackKetchupTea512",
  dataType: "jsonp",
  type: "GET",
  cache: {
    type: "persist",
    expires: 1000
  }
});

amplify.request("github-user", function(data) {
  console.log(data.data);
});

とすればGitHubのユーザーの情報を取得できる。

パラメータを使って書きなおすと

amplify.request.define("github-user", "ajax", {
  url: "https://api.github.com/users/{user}",
  dataType: "jsonp",
  type: "GET",
  cache: {
    type: "persist",
    expires: 1000
  }
});

amplify.request(
  "github-user", 
  { user: "BlackKetchupTea512" },
  function(data) {
    console.log(data.data);
  }
);

こんな感じ。

以上。

終わり

これで大体の機能の説明は終わり。機能のカスタマイズとかajax通信時のより詳細なコールバック設定とかデータの送信とかが説明できてないけどそこらへんは公式サイトを見てください。

Rails3.2でなにか作る(1)

Qiitaのアドベントカレンダーを見ているとなにか作りたくなったので。いままでのRailsの経験はアジャイル本をひと通り読んでMarkdownブログを作ってみたぐらい。一言でいうとビギナー。


では早速いくつかの良さげなプラグインの使い方を学ぶために小さなアプリを作っていく。「複数人のユーザーが投稿できるブログアプリ」を作るのが目標。

使いたいプラグインの一覧

アプリ作成

まずは作成。

$ rails new our-blog

Devise導入

初めにDeviseを導入してみる。Deviseについては次のサイトや記事が参考になった。

まずはGemfileをいじる。gem 'jquery-railsの下あたりに追加。バージョン指定はサンプルアプリの受け売り…。

# Gemfile
...
gem 'devise', '>= 2.1.2'
...

で、bundle installしてrails g devise:installする。

$ bundle install
…
$ rails g devise:install
…

Deviseをインストールすると次にするべきことを指示されるので一応従う。

  1. 確認用のメーラのための設定?やらなくても当分問題ないと思うけど、念のためconfig/environments/development.rbにconfig.action_mailer.default_url_optionsを追加。productionはとりあえず後回し。
  2. 未ログインのユーザーをリダイレクトさせるためにroot用のコントローラーを設定する。
    • とりあえず$ rails g controller home indexして、
    • config/routes.rbにroot :to => "home#index"を追加。
    • public/index.htmlが邪魔なので削除。
  3. ログイン後のフラッシュメッセージなどの表示設定。Twitter Bootstrap導入後に設定する。
  4. Rails3.1でHerokuにデプロイする場合の設定。3.2を使っているので飛ばす。

これで導入完了。

Deviseによる認証機能の作成

さっそく認証機能を作る。

まずは認証用のモデルを作成。

$ rails g devise User

これで認証用のモデルが生成できるようになる。けど、このままだとUserにはメールアドレスとパスワードしか登録できなくて寂しいのでUserモデルにnameカラムを追加するマイグレーションファイルを作る。

$ rails g migration AddNameToUsers name:string

これでマイグレートする。

$ rake db:migrate

新しくカラムが増えたのでapp/models/user.rbのattr_accessibleに:nameを追加。それと、ユーザー登録や編集用のビューも変更する。

元来Deviseはエンジンのため、ビューはGemパッケージのなかに内包されている。けど、ありがたい規約のおかげでapp/views/deviseにビューファイルがあればそっちを優先して描画してくれる。

また、このビューファイルの生成のためのコマンドも用意してくれているので、それを使ってビューファイルを編集すればいい。コマンドは次のようになる。

$ rails g devise:views

これでapp/views/devise以下にdevise用のビューファイルがザクザクと入ってくる。変更するべきなのは登録・編集用のビュー(registrations)だけなので、これとこれの描画に必要な共通ビュー(shared)以外のディレクトリは削除しておく。

ビュー(edit.html.erbとnew.html.erb)の編集は簡単で、フォームに下のような名前用のフィールドを追加するだけ。

  <div><%= f.label :name %><br />
    <%= f.text_field :name %></div>

これで認証機能はOK(バリデーション設定してないけど)。

Twitter Bootstrap導入

Twitter Bootstrap Railsを導入する。

参考サイト

Gemfileにgem 'twitter-bootstrap-rails' を追加。assetsグループの中に入れるのがよさそう。

次にコマンドラインで

$ bundle install
…
$ rails g bootstrap:install
…

と入力すれば導入完了。

layouts/application.html.erbの編集

最初にちょこっとapp/stylesheets/のapplication.cssとbootstrap_and_overrides.css.lessを編集する。application.css

 .main {
   margin-left: 5px;
 }

を追加して、bootstrap_and_overrides.css.lessから

body { 
	padding-top: 60px;
}

@import "twitter/bootstrap/responsive";

の部分を削除する。デザインの好みの問題なのでやらなくても問題ない。むしろやったほうが問題起きるかもしれない。

次にapp/views/layouts/application.html.erbを編集する。ユーザーがログインしていたらユーザーの情報をナビバーに表示、ログインしていなかったらログインへリンクを表示、といったレイアウトを作る。application.html.erbの書き方はここが参考になる(実は使ってるTBSのGemが違うけど、まあ問題ない)。

使ったDeveseの機能

ここを参考に。

user_signed_in? … 文字通りユーザーがサインインしてるかどうか。

current_user … 今のユーザーのモデル。current_user.nameとすると名前を表示できたり。

new_user_session_path … いわゆるログイン画面へのパス。

new_user_registration_path … 新規登録画面へのパス。

edit_user_registration_path … ユーザー編集画面へのパス。名前やパスワードを変えるやつ。

destroy_user_session_path … ログアウトのためのパス。セッションを破棄するという副作用があるため、DELETEメソッドを使う(こういうのを「れすとふる」な実装というらしい)。

これでlayoutは一応完成。

Scaffold!

ようやくアプリの開発に取り掛かれる。ブログ記事の大まかな土台をサクッとscaffoldで作る。

$ rails g scaffold Article title:string body:text user_id:integer
…
$ rake db:migrate
…

Userとのリレーションを設定する。app/models/の中、users.rbにhas_many :articlesを追加、article.rbにbelongs_to :userを追加。これで@article.userとかcurrent_user.articlesとかできるようになる。

Articleコントローラーとビューの編集

ログイン機能に合うようにコントローラーやビューを編集していく。

ゲストユーザーの制限

まず、ログインしていないユーザーはindexとshowしか見れないようにする。articles_controller.rbにbefore_filterを使ってauthenticate_user!をするように設定すればいい。

  before_filter :authenticate_user!,  :except => ["index", "show"]

記事作成者の処理

  • createのとき、新しいarticleのuser_idをcurrent_user.idに設定。edit,update,destroyのとき、対象のarticleのuser_idがcurrent_user.idと違う場合403コードを返す。

  • また、ビューのarticleの_form.html.erbの中のuser_id入力用のフィールドを削除しておく。

思い付く必要な処理はこれぐらい。他にもあるかも。

とりあえずこれでブログ機能は完成。

ActsAsTaggableOnの導入

タグ機能を追加してみる。タグ機能の実装はhas_and_belongs_to_manyを使ってもいいけど、やっぱりプラグインを使ったほうが便利そうなのでそっちにする。

参考サイト

Gemfileにgem 'acts-as-taggable-on'を追加。

次のようにコマンドを実行。

$ rails g acts_as_taggable_on:migration
…
$ rake db:migrate
…

これで導入完了。

記事にタグ機能を追加

ActsAsTaggableOnでタグ機能を追加するには、対象のモデルファイルに次のよう記述すればいい。

acts_as_taggable_on :タグの名称

例えばこう、複数指定もできる。

acts_as_taggable_on :tags, :categories

:tagsの場合は次のようなエイリアスも用意されている。

acts_as_taggable

これをapp/models/articl.rbに追加。ついでにattr_accessibleに:tag_listを追加しておく。

使い方

Articleからタグを取得する方法は二つある。

article.tagsとすれば、その記事が持つタグのモデルオブジェクトの配列を取得できる。

article.tags
=> [#<ActsAsTaggableOn::Tag id: 1, name: "rails">, #<ActsAsTaggableOn::Tag id: 2, name: "ruby">, #<ActsAsTaggableOn::Tag id: 3, name: "web">] 

article.tag_listとすればその記事が持つタグの名前(文字列)の配列を取得できる。

article.tag_list
=> ["rails", "ruby", "web"]

tag_listにタグの配列や文字列を代入してsaveすればタグが追加される。配列を渡す代わりにコンマで区切られた文字列を渡してもいい。

article.tag_list = "rails, ruby"
article.tag_list << "web"
article.save

article.reload
article.tags
=> [#<ActsAsTaggableOn::Tag id: 1, name: "rails">, #<ActsAsTaggableOn::Tag id: 2, name: "ruby">, #<ActsAsTaggableOn::Tag id: 3, name: "web">] 

次に記事のタグを編集・閲覧できるようにビューを書き換える。

_form.html.erbに次のようなフィールドを追加する。

  <div class="field">
    <%= f.label :tag_list %><br />
    <%= f.text_field :tag_list %>

そして、indexやshowのビューにタグを表示する記述を追加する。これだけ。コントローラーは変更しなくてもいい。

終わり

長くなったのでここまでで一旦終わり。あとでソースもGitHubに上げるつもり。

post script

はてなブログがMarkdown記法対応になって快適。はてなブログ(・∀・)イイ!!

任天堂のCode PuzzleをHaskellで

任天堂Code Puzzleというのをやっているようなので挑戦してみた。Pythonはまだ勉強中でよく分からないからHaskellを使って解いていたんだけど、最後の最後で、もうHaskellじゃむりぽという壁にぶち当たったので、区切りとしてブログにざっと経過を書いておく。

1

まあなんとかできた。

2

これは酷い問題だった。Hakellでやる場合あのややっこしいコードを再実装するのが大変。

3

2に比べれば大分楽だった。ヒントのおかげ:-)

4

これも楽。

?

なぞなぞ。かなり難しかったけど、ところどころに置いてくれてるヒントに励まされながらなんとかクリア。

??

はい思いっきり壁にぶち当たりました。これはHaskellじゃちょっとどうやっていいか分からない。

というわけで降参です。せっかくここまで来れたのに残念。

ちょっと一旦tea breakをとって、心の整理がついたらPythonで書き直して解いてみようかな。

ソース → nintendo-code-puzzle1

チャットアプリ書き直し

前回一応チャットアプリを作ったけど、あんまりExpressの動きが理解できなかったので、Nodeビギナーズブック公式ガイド公式リファレンスを読んでちゃんと入門してからもういちどチャットアプリを作っていく。

Expressでhtmlファイルを表示する方法

だけど、いきなりhtmlを表示させるところでつまづいた。何の気なしに

res.render('index.html');

みたいなことしようとすると下のようになる。

Error: Cannot find module 'html'

どうも「htmlという拡張子を持つファイル」に割り当てられているテンプレートエンジンが見つからないからエラーになるらしい。

結論

結論から言えば、htmlなどの静的なファイルは

app.use(express.static(__dirname + '/public'));

として、/publicフォルダ以下に置くのがベストということが分かった。

以下その結論に辿り着くまでの顛末。

経緯

そういえばただのhtmlファイルを表示するにはどうするんだろうと思って適当にググってみると、同様の問題がstackoverflowで質問されているのを見つける。回答を見てみると何通りも答えがあって、これといったベストな方法は決まってないらしい。いくつか回答をピックアップする。

app.engine()でhtml拡張子を既存のテンプレートエンジンでレンダリングするようにする

app.engine()(旧名register)は拡張子とテンプレートエンジンを結びつけるメソッド。これを使って.htmlも既存のテンプレートエンジンでレンダリングしてもらうようにするという方法。評価:18

app.engine('html', require('ejs').renderFile);
app.engine('html', require('jade').__express);

app.engine()に自作のhtml用テンプレートエンジンを指定する

上とやることは同じ。けど、ただのhtmlをわざわざテンプレートエンジン使ってレンダリングするのってパフォーマンス的にどうなのってことで、渡された文字列をそのまま返すだけの簡単なhtml用テンプレートエンジンを即興で作ってそれを利用するという方法。突っ込みコメントあり。評価:26

fs.readFile()でファイルを読み込んでres.sendする

そのまんま。だけど、ファイルがキャッシュされないからダメだと指摘されていた。評価:10

express.staticを使う(パフォーマンス的にベスト)

上の方法はどれもなんだかなあって感じの解決法ばっかりで、もういっそのこと拡張子をejsに変えるだけでよしとしようかなあと思っていたら、突っ込みコメントを見てexpress.static()があったことを思い出す。ググって使い方を調べてみると、まさにやりたいことそのものと言えるような内容だった。早速、app.jsに

app.use( express.static(__dirname + '/public') );

を追加。これでpublic/フォルダにindex.htmlを置いておけば、http://localhost/にアクセスしたときにindex.htmlが表示されるようになる。ちなみに、パス関係の便利モジュールを使って次のように書いてもいい。

var path = require('path');
app.use( express.static(path.join(__dirname, 'public')) );

というわけでいとも簡単にできた。ソース → simple_chat_room_2