Original Article in English: http://www.onlamp.com/pub/a/onlamp/2005/03/03/rails.html

Rolling with Ruby on Rails, Part 2 日本語訳

authored by Curt Hibbs
03/03/2005
translated to Japanese by Yoichiro Hasebe
04/15/2006

やあ、また会ったね!

Rolling with Ruby on Rails では Ruby on Rails でできることのごく一部しか扱わなかった。データ・バリデーションやデータベース・トランザクションについて何も語らなかったし、コールバック、ユニット・テスト、キャッシングについても述べなかった。また、Rails に含まれる数々の便利なヘルパーについてほとんど全く触れていない。本記事でもスペースの都合上、すべてを十分に説明することはできないけれど、いくつかの項目について詳しく説明するとともに、残りのものについては簡単な解説とより詳細な情報へのリンクを提供したいと思う。

訳者注: Rolling with Ruby on Rails, Part 1 の日本語訳は tanabe sunao さんがこちらで公開されています。

また前回は Ruby 言語について深く立ち入ることをあえてしなかった。Ruby on Rails のコードの背後でどのようなことが起こっているか簡単にでも知りたい人は、Amy Hoy のブログ・エントリー、Really Getting Started in Rails に目を通すことを強くお奨めする。

さて上の話題に移る前に、Part 1 で宿題にしておいた次の問題を片付けてしまおう。

注意深い読者の指摘により次のことが分かった。足場(scaffolding)によって提供された new recipe アクションにはカテゴリー設定の機能が無いため、カテゴリー分類を加えたことで新しいレシピを作成できなくなった。また list recipes アクションを呼ぶとエラーが出る。これらについても修正を加えないといけない。

準備はいいだろうか?では始めよう!

Ruby on Rails をアップデート

本記事の Part 1 を書いたとき、Rails のバージョンは 0.9.3 だった。現在のバージョンは 0.10.0 で、いくつかの便利な機能が加わっている。本記事では Rails 0.10.0 を使用する。もしあなたが2005年2月24日以降に Rails をインストールしたのであれば、すでに 0.10.0 がインストールされていることだろう。

Figure 1 はどんな RubyGems がインストール済みか(加えてそれらのバージョン名を)調べる方法を示している。Part 1 のときと同様、ここでは Windows システム上で操作を行うので、異なるプラットフォームの人は適宜読み替えてほしい。

listing installed RubyGems
Figure 1. インストール済みの RubyGems をリストアップ

コマンドライン画面を開いて次のコマンドを実行しよう。

gem list --local
ひとこと: gem list --remote コマンドは、rubyforge.org のリモートサーバーにある利用可能な RubyGems をすべてリストアップしてくれる。

もし Rails 0.10.0(またはより新しいバージョン)をインストールしていなければ、次のコマンドを実行する必要がある。

gem install rails

MySQL のセキュリティ・アップデート

Part 1 の執筆時、Rails は MySQL の新しいパスワード・プロトコルをサポートしておらず、記事の中では MySQL の root パスワードを空白にしておくよう指示した。これについて多くの人が不満を述べたし、さらに悪いことに、Windows 版 MySQL のパスワード脆弱性を狙ったウィルスが現在報告されている。

幸いバージョン 0.9.4 以降、Rails は新しいパスワード・プロトコルをサポートするようになった。

新しい scaffold 機能

あまり詳しく解説することはできないが、ぜひ知っておいて欲しいRailsの新しい scaffold 機能がある。これは例を使って見ていくのが一番分かりやすい。

Part 1 ではレシピのモデルとコントローラを次のコマンドで生成した。

ruby script\generate model Recipe
ruby script\generate controller Recipe

さらに RecipeController クラスに scaffold :recipe を挿入することで足場(scaffolding)を構築した。これにより即座に簡便な CRUD コントローラとビューテンプレートが提供されるのだった。 なおこれらのコードの細部は隠されていて見ることができない。

訳者注: Part 1 を読まれた方はご存知のとおり CRUD というのは、作成(Create)、読み込み(Read)、更新(Update)、削除(Delete)を意味する頭字語です。

上の手法は現在も有効だが、別の方法がある。次のコマンドを実行してみよう。

ruby script\generate scaffold Recipe

このコマンドは、モデルとコントローラに加えてすべての CRUD 操作が可能な scaffold コードとビューテンプレートを生成する。そのおかげで、コードを実際に確認ながら必要に応じ修正を加えていくということができるようになる。ただし、新たに生成される scaffold コードによってファイルが上書きされてしまわないよう、既にモデルやコントローラやビューテンプレートが存在しているときには十分注意してほしい。

レシピ・アプリケーションの完成

それではレシピ・アプリケーションを完成に近づけていこう。役に立つ Rails の各種機能についてはまたその後で述べることにする。

このアプリケーションをディレクトリ c:\rails\cookbook に作成したのを覚えているだろうか。本記事に登場するすべてのパスはそこを基本ディレクトリとしている。もし別の場所を用いるのであれば、以下に出てくるアプリケーションパスに修正を加えることを忘れないように。

本記事の my cookbook アプリケーションのソースコードをまとめたものを zip ファイルとしてダウンロードすることができる。Rails 0.13 またはそれより新しいバージョンで動作するようになっているので、古いバージョン使っている人はアップグレードしておくことをお勧めする。

Part 1 を読まずにソースコードだけダウンロードしてやろう考えている人(そう、あなたのことですよ)は、MySQL に cookbook という名前のデータベースを作成した上で、cookbook.sql を用いてデータを挿入しておいてほしい。

カテゴリーつきの新レシピを作成する

足場(scaffolding)を利用して作ったこれまでのコードでは、新しいレシピを作成する際に各レシピに対しカテゴリーを付与することができない。これ自体はそんなに困ることではないかもしれない。ただ各レシピが何らかのカテゴリーに属していないと レシピリストを表示するページでエラーが発生する。そのため今の状態のままだと新しい要素を加えた後にレシピをリストアップしようと思っても上手くいかない。

この問題は edit アクションを実装したときと同様、新しいアクションを作成し足場(scaffolding)を上書きすることで修正可能だ。Figure 2 のように、c:\rails\cookbook\app\controllers\recipe_controller.rbnew メソッドを追加しよう。

the Recipe controller's new method
Figure 2. RecipeControllernew メソッド

ここで @recipe = Recipe.new というコードは新しい空の recipe オブジェクトを生成してインスタンス変数 @recipe に代入している。Recipe クラスが recipes テーブルの行に対応していることに注意してほしい。これにより Recipe クラスは新しいrecipeオブジェクトを作る際、ビューテンプレートから利用可能なデフォルト値を各フィールドに設定することができる。

今のところ Recipe クラスはそのようなデフォルト値を設定していないが、これから見ていくビューテンプレートでは @recipe オブジェクトの値が何であれそれをそのままフォームの表示に使用する。また後でレシピの作成時に表示されるデフォルト値を指定しておくといいだろう。

edit アクションのときと同じく今回も全カテゴリーをまとめて取得し、ユーザが自由に選べるドロップダウン・リストを作成することにしよう。インスタンス変数 @categories にカテゴリーリストを保持する。

ディレクトリ c:\rails\cookbook\app\views\recipenew.rhtml というファイルを作って、下の HTML テンプレートを保存してほしい。大部分はスタンダードな HTML だが、カテゴリー選択のドロップダウン・リストを実現する <select> タグと <option> タグを生成するためのコードが加えられている。

<html>
 <head>
 <title>New Recipe</title>
 </head>
 <body>
 <h1>New Recipe</h1>
 <form action="/recipe/create" method="post">
 <p>
 <b>Title</b><br/>
 <input id="recipe_title" name="recipe[title]" size="30" type="text" value=""/>
 </p>
 <p>
 <b>Description</b><br/>
 <input id="recipe_description" name="recipe[description]" 
    size="30" type="text" value=""/>
 </p>
 <p>
 <b>Category:</b><br/>
 <select name="recipe[category_id]">
 <% @categories.each do |category| %>
 <option value="<%= category.id %>"> 
 <%= category.name %>
 </option>
 <% end %>
 </select>
 </p>
 <p>
 <b>Instructions</b><br/>
 <textarea cols="40" id="recipe_instructions" name="recipe[instructions]" 
    rows="20" wrap="virtual">
 </textarea>
 </p>
 <input type="submit" value="Create"/>
 </form> 
 <a href="/recipe/list">Back</a> 
 </body>
</html>

Part 1 で見た edit テンプレートとそれほどの違いはない。ここで recipe の date を含めなかったのは、ユーザがフォームをアプリケーション側に送信するのに合わせてそれをセットしたいからだ。そうすれば常に作成日の日付に保つことができる。

form タグを見ると、パラメータが recipe コントローラの create アクションに送られることがわかる。c:\rails\cookbook\app\controllers\recipe_controller.rb を開いて create メソッドを付け加えておこう。

def create
    @recipe = Recipe.new(@params['recipe'])
    @recipe.date = Date.today
    if @recipe.save
        redirect_to :action => 'list'
    else
        render_action 'new'
    end
end

上のメソッドはまず新しい recipe オブジェクトを生成し、それを new.rhtml のフォームから送られたパラメータで初期化する。その後 date を今日の日付に設定し、オブジェクトをデータベースに保存する。もし保存が失敗したら new アクションにリダイレクトするのでユーザは再びトライすることができる。

では確認だ。コマンドライン画面を開いて Web サーバを開始し、c:\rails\cookbook に移動した上で ruby script\server コマンドを実行する。そうしたら Web ブラウザから http://127.0.0.1:3000/recipe/new にアクセスし、Figure 3 のようにレシピを追加してみよう。

adding a new recipe with a category
Figure 3. カテゴリー付きの新しいレシピを追加する

新しいレシピを作成したら、画面は Figure 4 のようになるはずだ。

list of all recipes
Figure 4. 全レシピのリスト

レシピの削除

覚えているだろうか。Part 1 では足場(scaffolding)の list アクションを上書きしたためにレシピ削除の方法が無くなってしまった。そこでこの機能を list アクションに実装する必要がある。メインのリスト・ページの各レシピ名の後ろに小さい delete リンクを追加し、それをクリックすることでレシピ削除ができるようにしよう。簡単だ。

まず、c:\rails\cookbook\app\views\recipe\list.rhtml を開いて次のように delete リンクを加える。

<html>
 <head>
   <title>All Recipes</title>
 </head>
 <body> 
   <h1>Online Cookbook - All Recipes</h1>
   <table border="1">
    <tr>
      <td width="40%"><p align="center"><i><b>Recipe</b></i></td>
      <td width="20%"><p align="center"><i><b>Category</b></i></td>
      <td width="20%"><p align="center"><i><b>Date</b></i></td>
    </tr>
    <% @recipes.each do |recipe| %>
      <tr>
        <td>
        <%= link_to recipe.title, 
                    :action => "show", 
                    :id => recipe.id %>
        <font size=-1>
           
        <%= link_to "(delete)", 
                    {:action => "delete", :id => recipe.id},
                    :confirm => "Really delete #{recipe.title}?" %>
        </font>
        </td>
        <td><%= recipe.category.name %></td>
        <td><%= recipe.date %></td>
      </tr>
    <% end %>
   </table>
 <p><%= link_to "Create new recipe", :action => "new" %></p> 
 </body>
</html>

主な変更点は次のようにリンクを追加したことだ。

<%= link_to "(delete)", 
		{:action => "delete", :id=> recipe.id},
:confirm => "Really delete #{recipe.title}?" %>

前回と異なり、ここでは JavaScript による確認ダイアログを生成するオプションを用いている。ダイアログウインドウでユーザが OK をクリックすると処理が進む。Cancel をクリックするとアクションは生じない。

http://127.0.0.1:3000/recipe/list にアクセスして試してみよう。Ice Water recipe の delete をクリックし、ダイアログがポップアップしたら Cancel を選ぶ。Figure 5 のようになるはずだ。

confirm deleting the ice water recipe
Figure 5. Ice Water recipe の削除を確認する

今度は OK をクリックしてみよう。Figure 6 のようになっただろうか?

error deleting the ice water recipe
Figure 6. Ice Water recipe を削除しようとしてエラー

心配無用。こうなることは想定内だ。ミスをしても大丈夫ということを示したかったんだ。ビューテンプレートに delete アクションへのリンクを加えたものの、まだ recipe コントローラに delete アクションを作成していなかった。

c:\rails\cookbook\app\controllers\recipe_controller.rb を開いて次の delete メソッドを追加しよう

def delete
    Recipe.find(@params['id']).destroy
    redirect_to :action => 'list'
end

最初の行では、与えられた ID を持つ recipe を見つけ出して destroy メソッドを呼び出している。次の行では単純に list アクションへのリダイレクトを行っている。

では再び試してみよう。http://127.0.0.1:3000/recipe/list にアクセスして Ice Water レシピを削除してみる。Figure 7 のように、Ice Water レシピは消えて無くなるはずだ。

ice water recipe is gone
Figure 7. Ice Water レシピを削除

レイアウトの使用

Part 1 でカテゴリーに対するすべての CRUD 操作を足場(scaffolding)を使って実現した際、メインのレシピリスト・ページにリンクが作成された。けれどもレシピリストのページにだけリンクを設けるより、各ページの下部にまとめて設置したほうがずっと便利だ。Rails にはまさにそのために作られたレイアウトという機能がある。

共通のヘッダやフッタを全部のページにわたって表示しているサイトでは、ほとんどの場合、各ページにヘッダやフッタのテキストをインクルードしている。しかし Rails では逆にレイアウトファイルの方にページのコンテンツをインクルードする。これについては、実例を見たほうが分かりやすいだろう。

c:\rails\cookbook\app\controllers\recipe_controller.rb を開いて、Figure 8 のように layout 行をクラス定義の直後に挿入してほしい。

adding a layout to the recipe controller
Figure 8. recipe コントローラに layout を追加

これで、ページ表示のために standard-layout.rhtml ファイルを使用すべきことを recipe コントローラに伝えられる。Rails は c:\rails\cookbook\app\views\layouts\standard-layout.rhtml というパスを用いてファイルを探すので、まだ存在していない layouts ディレクトリを作っておこう。そしてその下に次の内容のレイアウトファイルを作成しておこう。

<html>
 <head>
   <title>Online Cookbook</title>
 </head>
 <body>
   <h1>Online Cookbook</h1>
   <%= @content_for_layout %>
   <p>
     <%= link_to "Create new recipe", 
                 :controller => "recipe", 
                 :action => "new" %>
     
   <%= link_to "Show all recipes", 
               :controller => "recipe", 
               :action => "list" %>
     
   <%= link_to "Show all categories", 
               :controller => "category", 
               :action => "list" %>
   </p>
 </body>
</html>

上のテンプレートがこれまでのテンプレートと異なるのは次の一行だけだ。

<%= @content_for_layout %>

このコードは recipe アクションが出力するコンテンツの挿入位置を指定する。それぞれのリンクでコントローラとアクションの両方が指定されていることに注意してほしい(これまではコントローラを明示せず、実行中のコントローラがデフォルト値として用いられていた)。もちろんこれが必要なのはカテゴリーリストのページへリンクを設定するためであり、他の2つのリンクについては省略形を使うことも可能だ。

実際に試す前にもう1ステップ踏まなければならない。レシピのビューテンプレートには現在レイアウトの中に含まれている HTML タグが残っているので、c:\rails\cookbook\app\views\recipe\list.rhtml を開き最初と最後の余分な箇所を削除しておこう。次のようになればいい。

<table border="1">
 <tr>
   <td width="40%"><p align="center"><i><b>Recipe</b></i></td>
   <td width="20%"><p align="center"><i><b>Category</b></i></td>
   <td width="20%"><p align="center"><i><b>Date</b></i></td>
 </tr>
 <% @recipes.each do |recipe| %>
   <tr>
     <td>
     <%= link_to recipe.title, 
                 :action => "show", 
                 :id => recipe.id %>
     <font size=-1>
        
     <%= link_to "(delete)", 
                 {:action => "delete", :id => recipe.id},
                 :confirm => "Really delete #{recipe.title}?" %>
     </font>
     </td>
     <td><%= recipe.category.name %></td>
     <td><%= recipe.date %></td>
   </tr>
 <% end %>
</table>

同様に、c:\rails\cookbook\app\views\recipe\edit.rhtmlc:\rails\cookbook\app\views\recipe\new.rhtml の余分な箇所も削除しておこう。form タグとその中の要素だけを残すようにする。

では http://127.0.0.1:3000/recipe/list にアクセスして、Figure 9 のようになるか確認しよう。

using a layout with common links
Figure 9. レイアウトを使って共通リンクを実現する

recipe コントローラが出力するすべてのページの下部に3つのリンクが現れるはずだ。ぜひ実際に試してみてほしい!

Show all categories をクリックしたときだけこの素敵なリンクが出てきてくれないことに気づいたかもしれない。これは category ページを表示するのが category コントローラであり、今のところ recipe コントローラ以外は新しいレイアウトについて知らないからだ。

これを修正するには、c:\rails\cookbook\app\controllers\category_controller.rb を開き、Figure 10 のように layout 行を追加すればいい。

adding a layout to the recipe controller
Figure 10. category コントローラに layout を追加する

これでレシピ・アプリケーションの全ページの下部に共通のリンクが表示されるようになった。

カテゴリーに属するレシピを表示する

最後の課題は特定のカテゴリーのレシピだけを表示する機能を付け加えることだ。メイン・ページの各レシピの隣に表示されるカテゴリー名をクリックするとそのカテゴリーに属するレシピがリストアップされるようにしたい。

それには レシピリストのビューテンプレートを修正して、カテゴリー指定用の URL パラメータを受け取るようにすればいい。またパラメータが省略されたときにはすべてのカテゴリーが表示されるようにする。まず、list アクションのメソッドを書き換えてビューテンプレートがパラメータを取得できるようにしよう。

c:\rails\cookbook\app\controllers\recipe_controller.rb を開いて list メソッドを次のように修正してほしい。

def list
    @category = @params['category']
    @recipes = Recipe.find_all
end

次に c:\rails\cookbook\app\views\recipe\list.rhtml を次のように編集する。

<table border="1">
 <tr>
   <td width="40%"><p align="center"><i><b>Recipe</b></i></td>
   <td width="20%"><p align="center"><i><b>Category</b></i></td>
   <td width="20%"><p align="center"><i><b>Date</b></i></td>
 </tr>

 <% @recipes.each do |recipe| %>
   <% if (@category == nil) || (@category == recipe.category.name)%>
     <tr>
      <td>
        <%= link_to recipe.title, 
                   :action => "show", 
                   :id => recipe.id %>
        <font size=-1>
           
        <%= link_to "(delete)", 
                    {:action => "delete", :id => recipe.id},
                    :confirm => "Really delete #{recipe.title}?" %>
        </font>
      </td>
      <td>
        <%= link_to recipe.category.name, 
                    :action => "list", 
					:category => "#{recipe.category.name}" %>
      </td>
      <td><%= recipe.date %></td>
     </tr>
   <% end %>
 <% end %>
</table>

2箇所の変更を加えることですべてのことを実現している。次の行を見てほしい。

<% if (@category == nil) || (@category == recipe.category.name)%>

ここではループ内のカレント recipe を表示すべきかどうかを判断している。category が nil のとき(すなわち URL に category のパラメータが設定されていないとき)、または URL パラメータに設定された category がカレント recipe の category にマッチするとき、recipe が表示される。

そして次が2点目だ。

<%= link_to recipe.category.name, 
    :action => "list", 
    :category => "#{recipe.category.name}" %>

ここでは list アクションを再び起動するための category パラメータつきリンクが生成されている。

http://127.0.0.1:3000/recipe/list にアクセスして、いずれかの Snacks リンクをクリックしてみよう。Figure 11 のようになるはずだ。

showing only snacks
Figure 11. Snacks だけを表示

それで?かかる時間は?

以上で完成!まずまず機能的なオンライン・クックブック・アプリケーションが記録的な短時間で開発できた。ちゃんと動作する大枠が仕上がったので、後は細部を磨いていくだけだ。

多くの解説やスクリーンショットに注意を奪われて、これらのコードが何を行い、どれくらい開発者の時間を節約してくれるかということが、いくらかぼかされてしまったかもしれない。この辺をはっきりさせるため、ちょっとした統計資料を示してみたい。

幸い、Rails には上のような疑問を解消するために有用な機能が組み込まれている。コマンドライン画面を開いてディレクトリ c:\rails\cookbook に移動し、次のコマンドを実行してほしい。

rake stats

示される結果は大体 Figure 12 のようになるのではないだろうか。ここで LOC とは lines of code を意味している。

viewing development statistics
Figure 12. アプリケーションの開発に関する統計

すべてを詳しく説明することはできないが、一番下に示された最も重要な数値について触れておきたい。

Code LOC: 47

これはこのアプリケーションのコードの行数が実質47であったことを表している(コメントやテスト・コードは含まない)。なお筆者がこのアプリケーションを作るのにかかった時間は約30分だ!今までに手にしたどんな Web アプリケーション開発フレームワークを使っても生産性をここまで上げることはできないと思う。

それでもこれを、ごく単純な素材を用いた特別な事例であると考える向きがあるかもしれない。小規模なプロジェクトなら大丈夫だろうが、大規模プロジェクトには決して使えないと。そのような人々には次のセクションを読んで考えをあらためてもらうことにしよう。

Ruby on Rails のサクセス・ストーリー

Rails は比較的新しいフレームワークだ。この原稿の執筆時において、最初のパブリックリリースから半年も経っていない。にもかかわらず素晴らしい機能の数々と高い安定性により、まもなくして活気あるコミュニティが形成された。そしてこの短い期間にいくつもの企業レベル・アプリケーションが Ruby on Rails で開発されている。

Basecamp

サイト自体から引用しよう。

Basecamp はプロジェクト(あるいは単なるアイディアでも構いません)を管理し、クライアント/プロジェクト間のエクストラネットを迅速に構築する Web ベースのツールです。あなたとクライアント(またはプロジェクトチーム)がディスカッションを行ったり、アイディア、スケジュール、to-do リストなどをパスワードで守られた領域で共有したりといったことができます。

Basecamp は Ruby on Rails による最初の企業レベル・アプリケーションだ。Rails の作者でもある Heinemeier Hansson が開発を担当した。 発表時においてそれは彼一人が2ヶ月間で書いた4,000行のコードから成るシステムであった。2004年の秋、Basecamp のユーザ数は10,000人を突破した。正確な登録ユーザ数は非公開だが、公式サイトによると現在数万人規模のユーザがいるとのことだ。

43 Things

43 Things は目標設定のためのソーシャル・ネットワーク・アプリケーションだ。現在6,000人の登録ユーザと、数十万人の非登録ビジターがいる。43 Things は3人のフルタイムの開発者が3ヶ月で開発した43,500 行のコードで構築されている。

Ta-da Lists

Ta-da Lists はシンプルで共有可能な to-do リストを提供するフリーのオンライン・サービスだ。XMLHttpRequest を用いてサーバ・アクセスの待ち時間を減らすことで、非常にレスポンスのよいユーザインタフェイスが可能になっている。1人の開発者が1週間で書いた579行のコードにより構築されている。

Snow Devil

Snow Devil はスノーボードと関連商品を扱うe-コマース・サイトだ。ビジネス開始がごく最近のことなので、どれだけのユーザがいるのかはまだわからない。2人の開発者が4ヶ月で開発した6,000行のコードにより構築されている。

CD Baby Rewrite

CD Baby は大成功を収めているインディーズ音楽のリテイル・サイトだ。1998年のビジネス開始以来、82,443人のアーティストたちによる CD を1,200万枚も販売してきた。 これによりアーティストたちに支払われた金額は1,200万ドルにのぼる。

CD Baby サイトは、もはや管理不可能に達しつつある90,000行もの PHP コードで構築されていた。そこで開発者たちは Ruby on Rails でリライトを行うことにした。開発に関する詳しい情報が得られるまでには至っていないが、CD Baby のオーナーが現在ブログ上でコード変換のプロセスと進行状況について語ってくれている。

結局のところ?

最終的にアプリケーションの動作にとって重要なのはフレームワークよりむしろ優れた設計だ。データベースのデザインとテーブルのインデックス化については熟考が欠かせない。データへのアクセス・パターンを分析し、データを戦略的に非正規化することも検討すべきだろう。また処理済のデータはできるだけキャッシュするよう心がけるべきだ。

Rails はプロトタイプを作成し迅速にアプリケーションを開発するための強力な機能をいくつも備えている。これにより開発者は、アプリケーション自体の機能について考えたり、パフォーマンス向上のためのチューニングを行ったりといったことにより多くの時間を費やすことができる。

Ruby on Rails の機能を少し味見

Rails にはこの2部構成の記事だけでは網羅しきれない数多くの機能がある。Rails のツールキットについてより包括的に知ってもらうため、ここではそのうちのいくつかについて触れるとともに、詳しい情報を得るためのリンクを示していきたい。

キャッシング

キャッシングとは処理済のデータ(計算、描画、データベース・コールなどの結果)を再利用することにより経済性を高め、アプリケーションのスピードを向上させる手法だ。Rails は粒度の異なる3種のキャッシング・システムを用意している。

バリデーションとコールバック

データベースに書き込む前には、手持ちのデータが正しく完全なものであることを確かめるためバリデーションを行うべきだ。Rails にはオブジェクトをアップデートしたり、データベースにフィールドを作成したりする前にデータを検証できる簡便なメカニズムが用意されている。詳しくは validation how-to に目を通すか、直接 validation API documentation にあたってほしい。

ActiveRecord コールバックは、データオブジェクトの状態を変更する操作の前後に呼び出されるべくオブジェクトのライフサイクルに組み込まれる一種のフックである。

トランザクション

ActiveRecord はトランザクションをサポートしている。ドキュメンテーションからそのまま引用しよう。

トランザクションは一連の SQL 文がまとまったアクションとして包括的に処理されるときにのみ操作を有効とする一種の保護ブロックである。一つの古典的な例は、2つの銀行口座の片方からの引き出しが成功したときだけもう一方への預け入れを実行するというものだ。トランザクションはデータベースの健全性を保証し、プログラムのエラーやデータベースのブレイクダウンを防ぐ。したがって、包括的に実行する(あるいは包括的に実行しないでおく)必要がある複数の SQL 文を処理する際には基本的にトランザクション・ブロックを用いるべきだ。

例として、次のようなコードを見ておこう。

transaction do
    david.withdrawal(100)
    mary.deposit(100)
end

テスティング

Rails はテストを念頭において開発されており、Web アプリケーションのテストをサポートする機能が備わっている。充実したオンライン・チュートリアルがあり、Rails アプリケーションをどのようにテストすればよいかを知ることができる。

ジェネレイタ

ジェネレイタはアプリケーションのためにコードを生成してくれるヘルパー・スクリプトである。私たちもコントローラやモデルを作るためにジェネレイタを使用してきたし、本記事の最初のセクションでは足場(scaffolding)を生成するための新しいジェネレイタを紹介した。

Rails はまたユーザにより作成されたアドオン・ジェネレイタをサポートしている。例えば Tobias Luetke による Login Generator は、ユーザ認証やログインを容易に実装するためのコードを生成してくれる。

セキュリティ

Web アプリケーションにおいてセキュリティが重要であるのは周知の通りだ。Ruby on Railsのサイトでは online security manual を公開しており、よくあるセキュリティ上の問題とその対策が述べられている。

最後に

Rails は月並みなフレームワークでもなければ、単に実験的なフレームワークでもない。新しいレベルのフレームワークであり、これを使うことで開発者は確実に短期間で Web アプリケーションを構築できる。一人の開発者が複数人から成るチームよりも高い生産性を上げることさえ可能だ。そして何より、MIT ライセンスのもとに今すぐ利用できる。

近年のプログラミング史においてこれほどまで生産性を向上させるものはなかったのではないかと思っている。

編集者注: Ruby on Rails についてもっと知りたい方は Ajax on Rails もどうぞ。

参考資料

Web サイト
メーリングリスト

Curt Hibbs はセントルイス在住のシニア・ソフトウェア・エンジニアで、プラットフォーム、プログラミング言語、テクノロジーなど、数え切れないほどの分野において30年以上の経験を有している。