Rails2.0の足回りと中級者への道

第1回Rails2.0の足回り

Railsを使っている方も、使っていない方もこんにちは。この特集では、⁠Rails2.0の足回りと中級者への道」と題して、2007年12月7日にリリースされたRails2.0の基礎と、Rails2.0が提示する新しいアプリケーションの形、について学んでいきたいと思います。

Rails2.0とは何か?

Railsの開発者David Heinemeier Hansson(DHH)自身が語るように、Rails2.0は「革命的というより漸進的(⁠⁠evolutionary rather than revolutionary⁠⁠」なリリースです。バージョンの数値の変化とはうらはらに、Rails1.1とRails1.2間に行われたほどの変革はありません。

ですが、痛みすら伴う数多くの洗練と、RESTfulなアプリケーションへの傾倒を含んだリリースになっています。

この特集ではまず、Rails2.0で行われた変化を探り、Rails2.0の香りを感じていきたいと思います。

Rails2.0の進化と変化

とはいうものの、Rails2.0での機能追加は、はじめてRailsに触れる人にとっては非常に細かな変化がほとんどです。⁠Railsはじめまして」の方はしばらくご辛抱しておつきあいください。

ActiveRecord

Railsの中でももっとも大きく重要なフレームワークと目されているActiveRecordでも数多くの変更がなされました。

クエリーキャッシュ(Query Cache)

Rails2.0では、Railsの全てのactionの中で、自動でクエリーキャッシュが行われるようになりました。未設定の場合標準でクエリーキャッシュが行われます。

クエリーキャッシュとは、次のことをさします。

「SQLのSELECT文発行の結果を保持し、同じSQLが発行された場合はキャッシュの値を返すこと」

ただし、自動でクエリーキャッシュが行われるのは、Modelインスタンスの存続の間、通常は一つのリクエストの間に限られます。そのため複数のリクエストをまたぐようなキャッシュとしては利用できません。

キャッシュがクリアされるのは「INSERT、UPDATE、DELETEを発行したとき」です。結果が変更されてなくてもキャッシュはクリアされます。

実行時のイメージを、script/consoleで手動キャッシュを行うことで例示してみます。

リスト1 script/console
>> ActiveRecord::Base.cache {
      User.find(1) # DBから検索、結果がキャッシュに入る
    User.find(1)   # キャッシュヒット
 }

逆に、actionの中で、クエリーキャッシュを行いたく無い場合には以下のような工夫が必要です。

リスト2 app/model/user..rb
class User < ActiveRecord::Base
  class << self
    def force_find(*args)
      uncached { find(*args) }
    end
  end
end

リスト2では、Userモデルに、force_findメソッドを追加しています。Rails2.0では、uncachedブロックを指定した場合のみ、キャッシュが行われません。

非キャッシュのfindを実行したい場合は以下のようになります。

リスト3 script/console
>>User.force_find(:all)

ActiveRecordその他の進化

その他の変更点としては、次のようなものが挙げられます。

  1. 数値のバリデーションが便利に
  2. DBのスキーマ変更(マイグレーション)が簡単に(Sexy migration)
  3. テスト用データの作成が簡単に(FoxyFixture)

開発の生産性を向上させるこれらの機能については、次回以降紹介していきたいと思います。

ActionPack(ActionController、ActionView、ActionHelper)

つづいて、ActionController、ActionView、ActionHelperの変化を見ていきます。

Custom Action Delimiter

まず、進化ではなく変化ですが、Rails2.0では、RESTfulなルートでのカスタムアクションの区切り文字が変更になりました。⁠カスタムアクションの区切り文字」と言ってもピンとこない方も多いかと思います。具体例を挙げてみましょう。

リスト4 config/routes.rb
map.resource :posts, :collection=>{:comments=> :get}

上のルート定義が行われていた場合、Rails1.2では、区切り文字として「;」が使われていました。 Rails2.0ではこれが「/」に変更になりました。

リスト5 Rails1.2
GET  /posts;comments
リスト6 Rails2.0
GET /posts/comments

この変化の経緯は、⁠W3C HTML4.01仕様書の附属書Bに「&」の代用として「;」のサポートを求める⁠⁠、HTTPサーバへの実装者への推奨からです。そのため素直に、そのとおり実装しているHTTPサーバもあるわけです。ですが、Rails1.2のカスタムアクションの区切りはこれを無視して「;」を区切り文字として使用していました。

ですので、この件についてはRails2.0での変化というより「行儀がよくなった」と言うべきかもしれません。既に、Rails1.2でRESTfulなアプリケーションを作成されている場合は注意が必要です。

RESTful Routing

Rails1.2以降、RESTfulなアプリケーションのためRoutingにさまざまな機能が追加されています。Rails2.0ではさらにいくつかの機能が採用になりました。

  1. :name_prefixが不要に(自動でprefixがつきます)
  2. namespace機能(ルーティングにネームスペースが追加可能に)
  3. has_manyとhas_one Routing

ここでは、一番有用そうな、has_manyとhas_oneについて紹介したいと思います。

リスト7 Rails2.0
map.resources :articles,
    :has_many => [:comments, :tags],
    :has_one => :user

これは、Rails2.0以前の以下の記述と同じです。

リスト8
map.resources :articles do |article|
   article.resources :comments
   article.resources :tags
   article.resource :user
end

ActiveRecordで、関連を作成する、:has_many、:has_oneの対応と意味的に同型であるため、Rails2.0以降は、Routingで:resources、:resourceを使い分けるのではなく、:has_many、:has_oneを利用する方が明解であると思います。

Rails1.2以降採用になった、RESTfulなRoutingは短いスペースで紹介するには非常に複雑な機能であるため、次回以降再度取り上げたいと思います。

部分レイアウト(Partial Layout)

これは、render:partialの中身にたいしてさらに:layoutを適用することができる、という機能です。

リスト9 app/views/links/show.html.erb
<% for link in @links do %>
  <%= render :partial => 'link',
             :layout => 'link_summary',
             :locals => {:link => link} %>
<% end %>

上のコードが、メインとなるテンプレートです。ここで部分テンプレート(partial template)として、_link.html.erbを読み込んでいます。ここまではいままでどおりの部分テンプレートですが、見慣れない:layout指定があります。

この:layoutオプションで指定したレイアウトを適用し、部分テンプレートを表示します。

リスト10 app/views/links/_link.html.erb
<a href="<%=link.url%>"><%= link.title %></a>
<div id="comment"><%=link.comment%></div>

以下が、部分レイアウト(Partial Layout)です。ここで注意すべき点として、この部分レイアウトファイルは、app/views/layoutディレクトリではなく、コントローラ用のディレクトリに入れる必要があります。

リスト11 app/views/links/_link_summary.html.erb
<div id="link_<%= link.id %>">
  <%= yield %>
</div>

ActionPackその他の進化

そのほか、クライアントとサーバ間のレスポンスを向上させる以下の変更が組み込まれました。

  1. 画像用、CSS用といった複数のリソース用サーバがあるようにブラウザに思い込ませる(AssetServers)
  2. 複数のCSSや、Javascriptファイルを一つにまとめることで並列にダウンロードさせる(AssetCaching)
  3. セッションの保持がRuby PStoreからクッキーに変更(Cookie-based Session)

Ajaxを縦横に用いた即時のレスポンスが必要なアプリケーションでは重宝する拡張であると思います。

rake

Rails自身の変化につづいて、Railsの補助ツールであるRailtiesの変化についてです。

Rails2.0では、開発時のさまざまな局面で助けとなる、rakeタスクが追加されました。

routes(Route Listing Task)

Rails1.2以降、RESTful対応でRailsのRoute情報は複雑化の一途をたどっています。

このタスクではその複雑さを少しでも軽減するため、config/routes.rbで設定した情報を可視化します。

リスト12
% rake routes|grep DELETE
DELETE /hellos/:id                      {:controller=>"hellos", :action=>"destroy"}
DELETE /hellos/:id.:format              {:controller=>"hellos", :action=>"destroy"}

db:rollback

MigrationのバージョンをSTEP=Nで指定したNの分だけ過去に戻します。

リスト13
rake db:rollback STEP=1

Rails2.0以前は、このような相対的なバージョン指定ができなかったため、 現在のDBのバージョンを調べてから1減らした値を指定する、という面倒な作業を行う必要がありました。

Rails2.0以降では、db:rollbackを用いることでそういった局面での手数を減らすことができます。アジャイルなDB設計にとってはありがたい機能です。

notes:todo、notes:fixme

これはRails2.0で、ソースコードアノテーション(Source Code Annotations)といわれている機能です、コメント内のTODO、FIXMEと記述している箇所を一覧します。

リスト14
 % rake notes:todo
app/controllers/hellos_controller.rb:
  * [  5] implement

RailsCoreからなくなったもの

最後に、Rails2.0でフレームワークのコアからなくなってしまった機能について触れておきます。

フレームワークのスリム化のため、さまざまな機能が、Rails2.0ではcoreからはずされ、外部プラグインになりました。

例を挙げると、scaffold、acts_as_*プラグイン、in_place_editing、auto_completeやデータベースアダプターなどです。ただし、外部プラグイン化したとは言え、これらのものについてはgemでインストールするか、 script/plugin installでインストールしていけば良いだけです。

それらとは異なり、それら以上に重要で、かつ代替手段に決定打が無いのが、Railsで複数ページ遷移の管理を行うPaginationの消失です。Rails2.0ではPagination機能が削除されました。Rails標準のプラグインとしても提供されず、サードパーティの機能を利用することになります。

この一点のみで、Rails2.0への移行が踏み切れないプロジェクトも多いのではないかと思います。2008年4月時点での代替手段の選択肢は、will_paginateプラグインもしくは、paginating_findプラグインです。

Rails2.0インストール

かけあしで、Rails2.0の進化と変化を紹介してきました。それでは、ごたくはこの辺にしまして、Rails2.0のインストールを行いましょう。

RubyGemsのインストール

まず、Rubyのパッケージ管理システムであるRubyGemsをインストールします。メジャーなOS向けのパッケージが用意されていますので、ダウンロード、インストールしてください。

Rails2.0と必要なパッケージをインストール

RubyGemsのインストールが完了しましたら、gemを用い、Railsとその他必要なパッケージをインストールします。以下では、データベースアダプターとしてsqlite3、Railsのデバッグ用にruby-debugをインストールしています。 Rails2.0では標準のデータベースがsqlite3になっています。

リスト15
% gem install rails sqlite3 sqlite3-ruby ruby-debug

はじめての Rails2.0

それでは、すっかり有名になった Rails の scaffold を利用して、最初の Rails2.0 アプリケーション を作ってみます。

Rails2.0のscaffoldは、Rails1.2でscaffold_resourceと呼ばれていたRESTfulなコードを出力するscaffoldです。そのため、初見の方や、従来のscaffoldから移行される方にはやや敷居の高いコードになっています。ですが、今回の特集の目的であるRESTfulなアプリケーションの作成のサンプルにはもってこいです。

リスト16
% rails hello                                     # Railsアプリケーションフォルダ作成
% cd hello                                        # helloフォルダに移動
% ./script/plugin install scaffolding    # scaffolding pluginをインストール
% ./script/generate scaffold hello      # helloコントローラ、モデル、ビューを作る
% rake db:migrate                             # DBスキーマを作成

さてこれで準備はできました。

Railsアプリケーションをカスタマイズしていきましょう。

リスト17 app/controllers/hellos_controller.rb
# GET /hellos
# GET /hellos.xml
def index
   @greeting = "こんにちは"  # この行を追加

   @hellos = Hello.find(:all)
   respond_to do |format|
     format.html # index.html.erb
     format.xml  { render :xml => @hellos }
   end
end

こんどはViewを編集します。Rails2.0では、.rhtmlという拡張子と、.rxmlという拡張子が非推奨になりました。新しい拡張子はそれぞれ、.erb、.builderになります。Rails2.0では、.rhtml、.rxmlという拡張子もサポートされますが、今後のリリースではサポートが打ち切られれる可能性があります。Rails2.0のscaffoldでもVIEW用のテンプレートは.erbという拡張子のファイル名に変更されています。

index actionのなかに、あいさつを表示する、@greetingという変数を追加しています。

リスト18 app/views/hellos/index.html.erb
<h1>Listing hellos</h1>

<table>
  <tr>
    <th>Message</th>
    <th><%= @greeting %></th>  <!-- この行を追加 -->
  </tr>

<% for hello in @hellos %>
  <tr>
    <td><%=h hello.message %></td>
    <td><%= link_to 'Show', hello %></td>
    <td><%= link_to 'Edit', edit_hello_path(hello) %></td>
    <td><%= link_to 'Destroy', hello, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>
<br />

<%= link_to 'New hello', new_hello_path %>

まとめと次回予告

以上、簡単ではありますが、Rails2.0の香りを感じてみました。次回からは、Rails2.0の機能の詳細を紹介しながら、RESTfulなウェブアプケーションを作っていきたいと思います。

おすすめ記事

記事・ニュース一覧