Rails を使って Web アプリケーションに Ajax を組み込む

Ruby on Rails で Ajax を使うための実例

Ruby on Rails は、Web アプリケーションを構築するための素晴らしいプラットフォームを提供します。このプラットフォームに組み込まれた Ajax (Asynchronous JavaScript™ + XML) 機能を使って、Web 2.0 のリッチなユーザー・インターフェース・エクスペリエンスを皆さんのアプリケーションで実現しましょう。

Jack Herrington (jherr@pobox.com), Senior Software Engineer, Leverage Software

Jack D. Herringtonは、20 年以上の経験を持つシニア・ソフトウェア・エンジニアです。彼の著書には、『Code Generation in Action』、『Podcasting Hacks』、『PHP Hacks』の 3 冊があります。また、30 を超える記事も執筆しています。



2006年 12月 19日

まだ Rail について聞いたことがない人がいるとしたら、きっとその人は、この 1 年間を Ruby on Rails について聞かずに過ごせた唯一の場所、惑星 Zorton への旅から帰ってきた人に違いありません。Rails の最も魅力的な点は、Rails を使うことによって、アプリケーションとその機能を素早く作り上げられることです。Rails には Ajax のための Prototype.js ライブラリーが統合されて組み込まれているため、いわゆるリッチ・インターネット・アプリケーションを容易に構築することができます。

この記事では、Rails アプリケーションを構築するための手順を説明します。そして次に Ajax 機能を使って、サーバーとの間でデータを読み書きする JavaScript コードを作成します。

Rails について

ところで、Rails とは一体何なのでしょう。Rails は、Ruby プログラミング言語の上に構築された、Web アプリケーションのプラットフォームです。Ruby は約 10 年も前からあります。Ruby は Perl や Python と同じように、オープン・ソースでアジャイルなプログラミング言語であり、オブジェクト指向プログラミングを完全サポートしています。

Rails は、適切な Web アプリケーション・パターン (例えば MVC: Model-View-Controller など) を重要視する、アプリケーション・フレームワークです。この場合、システムのモデル部分は通常、データベースの表にマップされる一連の ActiveRecord オブジェクトで表現されます。コントローラー部分は Ruby クラスであり、モデルに対して実行される各種の操作それぞれに対するメソッドを持っています。そしてビューは通常、ERB テンプレートによって生成される HTML (Hypertext Markup Language) コードです (ERB は Ruby に組み込まれたテキスト・テンプレート・パッケージです)。このコードは、形式上は PHP や JSP (JavaServer Pages) のコードに似ています。またビューは、XML (Extensible Markup Language) コードであることもあれば、テキストや JavaScript コード、画像であることもあり、その他どんなものも可能です。

ユーザーが Rails Web アプリケーションからページを要求すると、URL はルーティング・システムをとおして送られ、ルーティング・システムはリクエストをコントローラーに送ります。コントローラーはモデルからデータを要求し、そのデータを、フォーマットするためにビューに送信します。

Rails アプリケーションを作成すると、システムは一連のディレクトリーや起動ファイルを自動的に生成します。そうした中には、システムに同梱されている JavaScript ファイル (Prototype.js ライブラリーも含まれます) のためのディレクトリーがあり、ビューとモデル、コントローラーのためのディレクトリーがあり、さらには、他の開発者からプラグインをダウンロードするのための場所まであります。


Rails を使い始める

Rails アプリケーションの構築を始めるためには、事前パッケージされた Rails システムの 1 つを使うのが最も簡単です。もし皆さんが Microsoft® Windows® を実行しているのであれば、Instant Rails を使うようにお勧めします。Macintosh に関しては、私は Locomotive 2 アプリケーションの大ファンです。これらのアプリケーションのどちらにも、Rails フレームワークや Ruby 言語、Web サーバー、そして MySQL が組み込まれています。ダウンロードは (正直なところ) 大きなものですが、それが終われば、新しい Rails アプリケーションの作成はごく単純です。

この記事では、Recipe (レシピ) という、新規の調理法のデータベース・アプリケーションを作成します。このデータベースには、表は 1 つしか必要ありません。リスト 1 は、Recipe アプリケーションのためのデータベースの移行を示しています。

リスト 1. データベースの移行
class CreateRecipes < ActiveRecord::Migration
  def self.up
    create_table ( :recipes, :options => 'TYPE=InnoDB' ) do |t|
      t.column :name, :string, :null => false
      t.column :description, :text, :null => false
      t.column :ingredients, :text, :null => false
      t.column :instructions, :text, :null => false
    end
  end

  def self.down
    drop_table :recipes
  end
end

このデータベースには、recipes という表が 1 つあるだけです。この表には 5 つのフィールドがあり、それらは name、description、ingredients、instructions ですが、5 番目のフィールドは Rails のインフラストラクチャーが自動的に管理する固有識別子です。

データベースの表が用意できたら、次はその表を ActiveRecord オブジェクトでラップします。このオブジェクトをリスト 2 に示します。

リスト 2. Recipe のモデル
class Recipe < ActiveRecord::Base
  validates_presence_of :name
  validates_presence_of :description
  validates_presence_of :ingredients
  validates_presence_of :instructions
end

ActiveRecord 基底クラスは、データベース・アクセスのための基本部分 (表のクエリーや、レコードの挿入、更新、削除など) をすべて処理します。ここでは単純に、個々のフィールドに対する検証を追加しており、Rails に対して、各フィールドがデータを持つ必要があると命令しています。


Ajax のフォーム

Recipe アプリケーションを構築するための最初のステップは、データベースにレシピを追加する方法を実現することです。その手始めとして、これを処理するための基本的な HTML フォームを Rails で作成する、標準的な方法を示すことにします。まず、リスト 3 に示す RecipesController クラスから始めましょう。

リスト 3. Recipes_controller.rb
class RecipesController < ApplicationController
  def add
    @recipe = Recipe.new
    if request.post?
      @recipe.name = params[:recipe][:name]
      @recipe.description = params[:recipe][:description]
      @recipe.ingredients = params[:recipe][:ingredients]
      @recipe.instructions = params[:recipe][:instructions]
      @recipe.save
    end
  end
end

ここには 1 つのメソッド、add しかありません。このメソッドは、空の Recipe オブジェクトを作成することから始まります。次にこのメソッドは、もしこのリクエストがクライアントからポストされているのであれば、パラメーターを追加して保存しようとします。

このページに対する ERB テンプレートをリスト 4 に示します。

リスト 4. Add.rhml
<html>
  <body>
<%= error_messages_for 'recipe' %><br/>
<%= start_form_tag %>
<table>
<tr><td>Name</td>
<td><%= text_field :recipe, :name %></td></tr>
<tr><td>Description</td>
<td><%= text_area :recipe, :description, :rows => 3 %></td></tr>
<tr><td>Ingredients</td>
<td><%= text_area :recipe, :ingredients, :rows => 3 %></td></tr>
<tr><td>Instructions</td>
<td><%= text_area :recipe, :instructions, :rows => 3 %></td></tr>
</table>
<%= submit_tag 'Add' %>
<%= end_form_tag %>
  </body>
</html>

このページはまず、recipe オブジェクトに対するエラー・メッセージから始まります。これらのメッセージが設定されるのは、Recipe モデル・オブジェクトに対する検証に失敗したデータをユーザーがポストした場合です。次にこのページは <form> タグを実行し、各フィールドに対する text_field と text_area 項目、<submit> タグを実行し、そしてフォームの終了を実行します。

これは非常に標準的な Rails です。安全でセキュアであり、どのブラウザーでも動作し、クライアントに対して作成された HTML に正確にマップすることができます。しかし私が望んでいるのは Web 2.0 であり、それはつまり Ajax を意味します。では、どのくらい変更が必要なのでしょう。

コントローラー側に関しては、add() メソッド用のコードは劇的に変わります。これをリスト 5 に示します。

リスト 5. Recipes_controller.rb
class RecipesController < ApplicationController
  def add
  end

  def add_ajax
    Recipe.create( { :name => params[:recipe][:name],
      :description => params[:recipe][:description],
      :ingredients => params[:recipe][:ingredients],
      :instructions => params[:recipe][:instructions] } )
  end
end

add() メソッドはもはや何もしません。新しい、add_ajax() と呼ばれるメソッドが、クライアントから返ってくるデータを処理するからです。

テンプレート側は、少し変わります。これをリスト 6 に示します。

リスト 6. add.rhtml の先頭
<html>
  <head>
  <%= javascript_include_tag :defaults %>
  </head>
  <body>
  <div id="counter"></div>
<%= form_remote_tag :url => { :action => 'add_ajax' },
  :complete => 'document.forms[0].reset();',
  :update => 'counter' %>
<table>
<tr><td>Name</td>

ファイルの先頭で、Rails デフォルトの JavaScript ファイルへの参照を含む新しい head セクションを HTML に追加します。これが Prototype.js システムであり、Ajax に関する作業の大部分を行います。

その後で、counter と呼ばれる <div> タグを追加します。このタグは、Ajax リクエストからの戻りの結果を保持します。これは必ずしも必要ではありませんが、ユーザーに何らかのフィードバックを与えるのは良いことです。

最後に、古い start_form_tag コールを form_remote_tag に変更します。form_remote_tag にはいくつかのパラメーターがありますが、最も重要なものは、データの送信先を指定する url 項目です。2 番目に重要なものは complete ハンドラーであり、これは Ajax リクエストが完了したときに実行される JavaScript コードを含んでいます。この場合には、ユーザーが別のレシピを入力できるようにフォームをリセットしています。次に、update パラメーターを使って、add_ajax アクションからの出力をどこに送信すべきかを Rails に命令しています。

また、add_ajax() メソッドのためのテンプレートも必要です。リスト 7 はこのテンプレートを示しています。

リスト 7. Add_ajax.rhtml
<%= Recipe.find(:all).length %> recipes now in database

そして、これで終わりです。本当にこれだけなのです。標準的な HTML フォームを Rails を使って Ajax フォームに移行するために必要なことは、これだけなのです。図 1 は、送信準備の整った、Ajax ベースのフォームを示しています。

図 1. Ajax フォーム
Ajax フォーム

次のステップは、より動的な対話動作、例えば Ajax を使った動的検索を試すことです。


Ajax を使った動的検索

Prototype.js は、ページ上のフィールドやフォームを監視するための機能を提供します。この機能を使って、レシピの名前の一部を入力できるテキスト・フィールドを監視します。このファイルは次に、RecipesController の検索メソッドを実行します (このメソッドは出力を、このテキスト・フィールドの下の <div> タグの中に置きます)。リスト 8 に示す、更新された RecipesController から始めることにしましょう。

リスト 8. Recipes_controller.rb
class RecipesController < ApplicationController
...
  def index
  end

  def search_ajax
    @recipes = Recipe.find( :all,
      :conditions => [ "name LIKE ?",
         "%#{params[:recipe][:name]}%" ] )
    render :layout=>false 
  end
end

index() メソッドは HTML フォームを描画します。search_ajax() メソッドは検索パラメーターに基づいてレシピを見つけ、そのデータを、フォーマットするために ERB テンプレートに送信します。index.rtml テンプレートをリスト 9 に示します。

リスト 9. Index.rhtml
<html>
<head>
<%= javascript_include_tag :defaults %>
</head>
  <body>
<%= form_tag nil, { :id => 'search_form' } %>
<%= text_field 'recipe', 'name' %>
<%= end_form_tag %>

<div id="recipe">
</div>

<%= observe_form :search_form, :frequency => 0.5,
     :update => 'recipe',
     :url => { :action => 'search_ajax' } %> 
  </body>
</html>

リスト 9 の先頭にも、JavaScript ライブラリーを含めてあります。その後、検索フィールドを持つ form フォームと、検索から返されるデータを保持する <div> タグを作成します。最後に、observe_form() ヘルパー・メソッドを呼び出します。このメソッドは、フォームの変化を監視してフォームからのデータを search_ajax() メソッドに送る JavaScript コードを作成します。このコードは次に、そのメソッドの結果を recipe <div> の中に置きます。

search_ajax.rhtml フォームのコードをリスト 10 に示します。

リスト 10. Search_ajax.rhtml
<% @recipes.each { |r| %>
<h1><%= r.name %></h1>
<p><%= r.description %></p>
<% } %>

検索の結果、複数のヒットが返る可能性があるため、レシピのリストを繰り返すことにし、レシピの名前と説明を出力しています。

ブラウザーでこのサイトを開き、アドレス・バーに apple を入力すると、アップル・コブラー (apple cobbler) という料理のレシピが見つかります。これを図 2 に示します。

図 2. Ajax の動的検索
Ajax の動的検索

基本的な実装は、これで終わりです。では、アップル・コブラーについて詳しく知りたかったら、どうすればよいのでしょう。Ajax を使えば、材料のリストや作り方を、オンデマンドで動的に得られるのでしょうか。よく質問してくださいました。もちろん、できるのです。


オンデマンドでコンテンツを追加する

場合によると、利用者に対してページ上で無条件に情報を提供するよりも、追加情報をダウンロードできるオプションを示した方が適切なことがあります。従来、Web アプリケーションの開発者は、そうした情報を含む隠し <div> タグを使い、ユーザーがさらに資料を要求したときにその情報を見せるようにしていました。Rails にはもっとスマートな、データが要求されたときに Ajax を使ってデータを取得するオプションがあります。

リスト 11 は、 link_to_remote() ヘルパー・メソッド・コールを追加したレシピ・テンプレートを示しています。

リスト 11. Search_ajax.rhtml
<% @recipes.each { |r| %>
<h1><%= r.name %></h1>
<p><%= r.description %></p>
<div id="extra_<%= r.id %>"></div>
<%= link_to_remote 'Extra',
    :url => { :action => 'get_extra_ajax', :id => r.id }, 
    :update => "extra_#{r.id}" %> 
<% } %>

link_to_remote() メソッドは、指定されたテキストを含むアンカー (<a>) タグと共に、JavaScript コードをページに追加します。利用者がリンクをクリックすると、このページは Ajax リクエストを行って新しいコンテンツを入手し、アンカー・テキストをそのコンテンツで置き換えます。

追加情報を入手するためには、get_extra_ajax() という、もう 1 つのメソッドを RecipesController に追加する必要があります。このメソッドをリスト 12 に示します。

リスト 12. Recipes_controller.rb
class RecipesController < ApplicationController
  ...
  def get_extra_ajax
    @recipe = Recipe.find( params[:id] )
    render :layout=>false 
  end
end

それと同時に、追加情報のための、get_extra_ajax.rhtml というテンプレートも必要です。リスト 13 はこのテンプレートを示しています。

リスト 13. Get_extra_ajax.rhtml
<blockquote><%= simple_format @recipe.ingredients %></blockquote>
<p><%= simple_format @recipe.instructions %></p>

さて、このページに行き、apple を入力すると、図 3 のようなものが表示されます。

図 3. 材料と作り方を取得する追加のリンク
材料と作り方を取得する追加のリンク

リンクをクリックすると、ブラウザーは Ajax を使って Web サーバーから追加情報を取得し、その位置に情報を表示します。このプロセスを図 4 に示します。

図4. レシピに関する追加の詳細
レシピに関する追加の詳細

各レコードの詳細を見つけるためのコストが高く、従ってオンデマンドで見つける方が理想的な行項目、あるいは詳細型のレポートがある場合、このタイプの Ajax パターンが特に便利です。また、この方法は、画面上の表示面積を小さくすることができます。

Web 2.0 で最もホットな機能は、おそらくテキスト・フィールドのオート・コンプリートでしょう。これを説明せずに Ajax の解説を終えるわけにはいきません。


オート・コンプリート・フィールド

Rails を使うと、オート・コンプリート・フィールドが驚くほど簡単に作成できます。最初に、index.rhtml テンプレートにいくつか追加する必要があります。更新されたテンプレートをリスト 14 に示します。

リスト 14. 更新された index.rhtml
<html>
<head>
<%= javascript_include_tag :defaults %>
<style>
div.auto_complete {
  width: 300px; 
  background: #fff; 
} 
div.auto_complete ul { 
  border: 1px solid #888; 
  margin: 0px; 
  padding: 0px; 
  width: 100%; 
  list-style-type: none; 
} 
div.auto_complete ul li { 
  margin: 0px; 
  padding: 3px; 
} 
div.auto_complete ul li.selected { 
  background-color: #ffb; 
} 
div.auto_complete ul strong.highlight { 
  color: #800; 
  margin: 0px; 
  padding: 0px; 
} 
</style>
</head>
  <body>
<%= form_tag nil, { :id => 'search_form' } %>
<p><%= text_field 'recipe', 'name', :autocomplete => 'off' %></p>
<div class="auto_complete" id="recipe_name_auto_complete"></div> 
<%= auto_complete_field :recipe_name,
     :url => { :action=>'autocomplete_recipe_name' },
     :tokens => ',' %>
<%= end_form_tag %>
...

ファイルの先頭にある CSS (Cascading Style Sheets) スタイル・ブロックは、オート・コンプリート項目のドロップダウン・リスト用に使われています。次に、ブラウザーの自動的なオート・コンプリート機能をオフにするために、text_field に少し追加をします。ここではドロップダウンの内容が入れられる <div> と、auto_complete() メソッドの呼び出しを追加しました。auto_complete() ヘルパー・メソッドは、クライアント・サイドの JavaScript コードを作成します (このコードは、recipe name テキスト・フィールドの現在の内容を使ってサーバーの autocomplete_recipe_name() メソッドをコールします)。

RecipesController の autocomplete_recipe_name() メソッドは、その name に対する検索を実行します。これをリスト 15 に示します。

リスト 15. Recipes_controller.rb
class RecipesController < ApplicationController
...
  def autocomplete_recipe_name
    @recipes = Recipe.find( :all,
       :conditions => [ "name LIKE ?",
          "%#{params[:recipe][:name]}%" ] )
    render :layout=>false 
  end
end

このコードには、リストを作るための、もう 1 つの ERB テンプレートが必要です。これをリスト 16 に示します。

リスト 16. Autocomplete_recipe_list.rb
<ul class="autocomplete_list"> 
<% @recipes.each { |r| %> 
<li class="autocomplete_item"><%= r.name %></li> 
<% } %> 
</ul>

オート・コンプリート・システムは、HTML の項目リスト (<ul>) を探します (<ul> では各項目が 1 つのオプションです)。これらの項目は、index.rhtml ページのCSS を使って (あるいは皆さんが提供するスタイルシートを使って) フォーマットされます。

オート・コンプリート・システムの実際の動作を見るために、ブラウザーを使ってそのページまでサーフィンし、そして test と入力します。何らかのデータが必要なので、テスト用のレシピを入力します。その結果を図 5 に示します。

図 5. オート・コンプリートのドロップダウン・リスト
オート・コンプリートのドロップダウン・リスト

上向き、下向きの矢印を使うとオプションを選択することができ、Enter キーで選択が確定します。そうすると、選択されたテキスト項目の内容がコピーされて再びテキスト・フィールドに入ります。

これはスマートです。しかも Rails のアーキテクチャーのおかげで、実装も非常に容易です。


まとめ

恥ずかしがらずに言いますが、私は Rails が大好きです。Rails を最初に使い始めた瞬間から、その魅力にとりつかれました。Web を見回すと、Rails の魅力にとりつかれた開発者は他にもたくさんいます。それは不思議なことではありません。Rails によって、対話性の高い Web アプリケーションが軽々と構築できるのです。

たとえ皆さん自身が Rails アプリケーションを製品として出荷することがないとしても、ぜひ Instant Rails あるいは Locomotive アプリケーションをダウンロードし、自分で試してみるようにお勧めします。非常に楽しい上、多くを学ぶことができ、皆さん自身の Java™ PHP あるいは Microsoft .NET アプリケーションの中で使いたくなるでしょう。もしかすると、フルタイムで Rails を書きたいと思うようになるかもしれません。

参考文献

学ぶために

  • Rails のホーム・サイト、Ruby on Rails.org を訪れてください。
  • Ruby 言語に関する資料の正式なソースおよびダウンロードのサイトとして、Ruby home page を訪れてください。
  • Agile Development with Rails: Agile Development with Rails』(Dave Thomas らの共著、2006年 Pragmatic Bookshelf 刊) は Rails の話題に関する必読の書です。
  • developerWorks XML ゾーンを利用して XML について詳しく学んでください。
  • XML および関連技術において IBM 認証開発者になる方法についてはこちらを参照してください。
  • XML 記事一覧をご覧ください。developerWorks XML ゾーンには、広範な話題を網羅した技術記事やヒント、チュートリアル、技術標準、IBM レッドブックなどが用意されています。
  • developerWorks technical events and webcasts で最新情報を入手してください。

製品や技術を入手するために

  • Windows 用のオールインワン・スターター・キット、Instant Rails を試してみてください。
  • Mac OS X 用のスターター・キット、Locomotive を入手してください。(Rails は Mac OS X V10.5 Leopard に搭載される予定です。)

議論するために

  • Ajax discussion forum で Ajax に関する話題の議論に加わってください。そして質問を投稿したり、他の人の質問に回答したりしてください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Open source, Web development
ArticleID=239859
ArticleTitle=Rails を使って Web アプリケーションに Ajax を組み込む
publish-date=12192006