前回の記事では、Rails2.0のscaffoldをベースに、RESTfulなdel.icio.usクローンのブックマークアプリケーションを作成してみました。今回は前回作成したアプリケーションをRails2.0の機能を用いて改良していきたいと思います。
リソース設計
前回作成したブックマークアプリケーションは、RESTの統一インターフェースにもとづいた、以下のリソースを持ちます。
機能 | HTTP Method | URI |
リンクの作成 | POST | /links |
リンクの表示 | GET | /links/:id |
リンクの変更 | PUT | /links/:id |
リンクの削除 | DELETE | /links/:id |
つまり、現在のminiciousはログインした本人のリンクのCRUDが可能な、それしかできないアプリケーションです。
ソーシャルブックマークと名乗っているにもかかわらず、一人の世界だけで完結しているのはあまりに寂しいものです。他のユーザのリンクも閲覧可能にしましょう。ただし、自分以外のユーザのリンクを編集できるのは問題ですから、作成、変更、削除が可能なのは自分のリンクだけとします。
これらを踏まえて、提供するリソースを拡張してみます。
機能 | HTTP Method | URI |
リンクの作成 | POST | /:username/links |
リンク一覧表示 | GET | /:username/links |
リンクの表示 | GET | /:username/links/:id |
リンクの変更 | PUT | /:username/links/:id |
リンクの削除 | DELETE | /:username/links/:id |
タグ一覧表示 | GET | /:username/tags |
タグの表示 | GET | /:username/tags/:id |
Routing map.resourceふたたび
前説で設計したリソースに対する、RailsのRouting設定はどうなるでしょうか?
具体的なコードに入る前に、RailsのRESTful Routingを作成する、map.resources, map.resouceの機能について再度まとめておきます。
特に、map.resouces、map.resouceでRESTfulなCRUDが作成されることはこれまで何度が見てきました。ここでは、これらのメソッドが持つオプションについて見ていきましょう。
:path_prefix
リソースがネストする場合に、リソースの前に付くパスを指定します。前節のリソース設計で、リンクリソース、タグリソースの前に、ユーザ名のパスを前置するよう修正しました。そういった場合のresoucesは以下のようになります。
ここで指定した、path_prefixはコントローラやビュー内でパラメータとして取得できます。
:has_many, :has_one
Routingのネストを行い、従属リソースを表現します。:has_manyオプションは複数のリソースを表現し、:has_oneは単一のリソースを表現します。
今回作成しているブックマークアプリケーションでは「ユーザ」をリソースとして提供していませんが、「ユーザ」のCRUDをRESTful Webサービスで行うアプリケーションも考えられます。そういった場合のRouting定義は以下のようになります。
ユーザリソースに、リンクとタグを従属させています。この場合のリソースは以下のように表現されます。
リソース抜粋
機能 | HTTP Method | URI |
リンクの作成 | POST | /users/:id/links |
リンクの表示 | GET | /users/:id/links/:id |
タグの表示 | GET | /users/:id/tags/:id |
:member
CRUD以外、すなわち作成、参照、更新、削除以外の処理を行いたい場合、map.resoucesで、:memberオプションを指定します。:memberオプションには、アクション名とHTTP MethodのHashを指定します。
例として、リンクを非表示に設定するケースを考えます。リンクの非表示を、hideカスタムアクションへのPUT Methodで表現すると決定した場合、以下のように設定します。
なお、このURIにはhideという「動詞」が入っているため、RESTの統一インターフェースの原則的にはあまり望ましくありません。
miniciousのRouting定義
これらの機能を用いた、前節のリソース設計に対するRouting定義は以下のようになります。
ActiveResource
ここで、ActiveResourceについて紹介しておきます。
ActiveResource とは、Rails2.0からRails Coreに導入された、RailsのRESTful routingと、XML表現を解釈するパッケージです。ActiveRecord は、RDBとRubyのオブジェクトとをマッピングするRailsのパッケージですが、それに対してActiveResourceは、XMLや、JSONで表現されたRESTfulな「リソース」をデータとして扱い、Rubyのオブジェクトとして抽象化します。
また、ActiveResourceの導入と入れ替わりで、RailsでSOAPや、XML-RPCといったWebサービスを管理するパッケージであった「ActionWebService」はCoreから除外されました(別途、gemsでインストール可能です)。
ActiveResourceにできること
ActionWebServiceの代わりとして導入されたActiveResourceですが、Webサービスを汎用的に取り扱うActionWebServiceと異なり、その機能は非常に限定されたものです。ActiveResourceにできることは、RailsのRESTful routing規約に準じたWebサービスを解釈し、Rubyのオブジェクトとして処理することです。Rails以外のWebサービスにも利用できますが、Railsと同様のRESTful URIを持つ必要があります。
今回作成したアプリケーションは、RailsのRouting機能(map.resources)を用いて作成したWebアプリケーションであるため、当然ActiveResourceに適合します。
ActiveResouceの利用例
それでは、ActiveResourceを利用してminiciousにアクセスしてみましょう。ここで注意が必要です。現在のminiciousアプリケーション内に、ActiveResourceを利用したクラスを作成すると、ActiveRecordとActiveResourceの命名規約からクラス名の重複が発生します。そのため冗長ではありますが、ActiveResource利用するために、クライアントアプリケーションフォルダをもうひとつ作成します。
つづいて、作成したminicious-esアプリケーション内でActiveResource::Baseを継承したクラスを作成します。設置場所に悩みますが、今回はModel的な利用になるためmodelsフォルダの配下に設置します。
これだけで、RESTfulなWebサービスの利用が可能となります。
コードを見ていきましょう。まず、ActiveResouce::Baseの特異メソッド「self.site=」で、Webサービスを提供するURIを指定します。今回作成した、ブックマークアプリケーションでのRESTfulな作成、更新、削除アクセスはBasic認証が必要となっています。そのため「http://」につづいて「ユーザ名:パスワード@」の文字列を追加し、Basic認証を行うことを知らせています。
つづいて、ActiveResouce::Baseの特異メソッド「self.prefix=」で、リンクのCRUDを行う接頭のURIを指定しています。miniciousのリソース設計では、ユーザ名がリンクのURIの前にくるため、prefixに「user」というユーザ名を直接指定しています。なお、prefixは「/」ではじまり「/」で終わる必要があります。
それでは、作成したActiveResouceのLinkクラスを利用してみましょう。ActiveResourceでは、ActiveRecordと同様のメソッドが利用できます。
ActiveRecord | ActiveResource | URI |
Link.find(:all) | Link.find(:all) | GET http://localhost:3000/user/links.xml |
Link.find(1) | Link.find(1) | GET http://localhost:3000/user/links/1.xml |
Link.exists?(1) | Link.exists?(1) | GET http://localhost:3000/user/links/1.xml |
Link.create(condition) | Link.create(condition) | POST http://localhost:3000/user/links |
ユーザ名「user」が「http://www.exampl.com/」へのリンクを作成し、タグとして「example」と「test」として分類する場合、以下のようになります。
いかがでしょうか。こういった形で、簡易にWebアプリケーションにアクセスできるのが、RailsでRESTfulなWebアプリケーションを作成することの醍醐味と言えます。
最終的なアプリケーションコード
ここまでの機能を含む最終的なコードの簡単な解説をしていきます。
モデル
まずは、モデルから解説します。
リンクを管理する、Linkモデルです。
代入メソッドtag_names=で、既存のタグを削除し、新規のタグを作成する付け直しの作業をしています。先にsaveメソッドを呼んでいるのは、has_many: throughで多対多の関連を作成した場合の制限によるものです。
また自身以外のユーザによる変更は発生しないはずですが、念のため Transaction ブロックで囲みトランザクション処理をしています。
取得メソッドtag_namesでは、リンクに関連付けられたタグのリストから、スペースで区切られたタグ名一覧を作成しています。この2点の修正で、Linkモデルにtag_names属性を追加します。
find_by_user_name_and_tag_idでは、ユーザ名とタグIDからリンク一覧を検索しています。この箇所だけではなく、miniciousでは積極的にfind_by_sqlを利用しています。
タグを管理するTagモデルのfind_by_user_nameメソッドでは、ユーザが持つタグの一覧を検索しています。SQLのCOUNT関数で、ユーザが持つ全リンク内のタグの件数を取得し、「cnt」というタグ件数を保持する属性を作成しています。ActiveRecordでは、Rubyのメタプログラミングにより「メソッドが生えてくる」ようなイメージで属性が利用できます。
コントローラ
つづいて、コントローラです。
リンクURIをコントロールするLinksControllerです。before_filterの:onlyオプションで認証を行うアクションの指定を行っています。更新系のアクションにのみ認証を設定しています。
上のコード例以外の修正として、URIの変更にともない、リダイレクトするパスの修正を行っています。
ERb View
最後に、ERbのviewです。
リンク一覧を表示する、index.html.erbです。
ここではURIの変更に関連するlink_pathの修正に注意が必要です。link_pathでは、path_prefixで指定した:usernameをパラメータから取得し、@linkオブジェクトとあわせて2つの引数を渡しています。また「:method=>:delete」としてHTML Formから疑似HTTP DELETEメソッドを発行するよう指定しています(hiddenフィールドに設定されます)。これは、GETとPOSTしかできないWebブラウザ制限へのRESTful URIでの対処法です。更新時、edit.html.erbのform_forでは同様に「:method=>put」を指定しています。
index.html.erbでは、Linkモデルで作成したtag_names属性と、Tagモデルで作成したcnt属性を利用しています。
画面イメージとソースアーカイブ
次の画面は、リンク一覧です。リンク下部にタグの表示と、右のカラムにはタグ一覧が件数とともに表示されています。
次の画面は、タグ一覧です。タグ付けされたリンクの一覧が表示されます。
ソースコードアーカイブ
以下が今回改良したminiciousソースアーカイブです。
ちなみに最終段階においてもminiciousにはユーザ管理の画面はありません。今回作成したminiciousは、RESTとRoy Fieldingに敬意を表して、Apache License 2.0としますので、興味を持たれた方は機能追加してみてください。
まとめと展望と次回予告
今回は、Rails2.0の機能を用いてRESTfulにブックマークアプリケーションを拡張しました。
今回作成したアプリケーションは、単体の機能だけみるとあまりに簡素過ぎるアプリケーションです。しかし、RESTfulなアプリケーションを作成する最大の魅力は、統一インターフェースによる拡張性や接続性の拡大によるシナジー効果にあります。
例えば、RESTfulにサービスのインターフェースを統一することで、RESTful Railsアプリケーションであれば同じライブラリで一括して対応可能なことが期待できます。そういったRESTful Railsに対応するRubyにおけるライブラリ例が先に述べたActiveResouceです。
残念ながら、Webブラウザ上で利用可能な、JavaSriptでのActiveResouceに相当するライブラリは現状ありませんが、そういったライブラリを利用する、RESTful RailsへのWebブラウザアドオンなどを考えてみるのも楽しいのではないでしょうか。
Railsによるアプリケーション作成の実例はこの辺にしまして、次回は、Railsで製品レベルのWebアプリケーションを作成する際に重要となるパフォーマンスの問題について考えていきたいと思います。