ActiveResourceで拡張子なしのURIを発行する方法

Ruby on Rails 2.0から標準搭載されたActiveResource。これを使うと、RESTful APIをActiveRecordのように叩くことができるようになる。自分でURIを作ってopenすることもせず、レスポンスを自分でパースしてオブジェクトを作ることもせず、数行の記述でRESTfulなサービスを利用することが可能になる優れものだ。ActiveResourceの詳細は、ここを見て欲しい。

さて、現状のActiveResourceは、僕にとって一つ気になる点がある。それは「URIが必ず拡張子付きで発行される」という仕様。例えば、

として、「Community.find(1)」と実行すると、

GET http://commusuke.eisbahn.jp/api/communities/1.xml

というURIでRESTfulサービスにアクセスしにいく。つまり、接尾語として「.xml」が自動的に付加される。これにより「レスポンスはXML形式で返してね」という意思表示になる。ちなみにこの拡張子は、format=メソッドを使って変更することができる。例えば、

としてあげて「Community.find(1)」と実行してあげれば、

GET http://commusuke.eisbahn.jp/api/communities/1.json

というURIが発行され、「JSON形式で返してね」という意思表示を行うことができる、という機構。

では「.xml」も「.json」も付けず、

GET http://commusuke.eisbahn.jp/api/communities/1

というURIでアクセスを行いたいとする。もちろんRESTfulサービスの中には、拡張子付きのリクエストを受け付けず、固定でXML形式のレスポンスを返してくる、という局面も存在するわけである。しかし、現状のActiveResourceでは、必ず何らかの拡張子を付けてURIを組み立てる、というコーディングになっていて、つまり拡張子付きを受け付けないRESTfulサービスを利用することはできない、ということになっているのだ。ちなみに、「self.format = nil」とかやってもダメ。

では、拡張子がないURIに対応するためのパッチを書くことができるかどうか、やってみることにした。結論から言うと、拡張子をつけないようにするこことができた。以下に、そのためのコードを紹介してみる。

まず、「self.format = :json」というように指定した結果、ActiveResource::Formatsモジュールに属しているフォーマッタモジュールが実際には適用される。XML形式とJSON形式が標準で搭載されていて、

  • XML形式 – ActiveResource::Formats::XmlFormat
  • JSON形式 – ActiveResource::Formats::JsonFormat

という対応になっている。上記のフォーマッタモジュールでは、下記のメソッドがそれぞれ定義されている。

  • extension – 拡張子を返す。
  • mime_type – MIME種別を返す。
  • encode – 受け取ったオブジェクトをエンコード(XMLやJSONに変換)する。
  • decode – 受け取った文字列をデコード(ハッシュに変換)する。

まずは、XML形式なんだけど拡張子はなし、というモジュールを自作する。XmlFormatモジュールを継承(?)して作ってみる。XmlFormatのメソッドはそのままに、extensionメソッドのみ再定義してnilを返却するようにしている。これは、例えば「app/controller/application.rb」ファイルの最後に書いておけば良いだろう。

format=メソッドに渡すシンボル(:xmlとか:jsonとか)は、camelizeされたものに「Format」が付けられた名前(:xmlならXml+Format)のモジュールが適用されるようになっている。つまり、上記のXmlNotExtensionFormatモジュールを使用したい場合は、

というように指定してあげれば良い。これにより、拡張子が取り除かれたURIが作られる。

・・・と思いきや、世の中そんなに甘くなかった。extensionメソッドがnilを返したことによって「xml」という文字は取り除かれるようになるのだが、「.」が残ってしまう。つまり、

GET http://commusuke.eisbahn.jp/api/communities/1.

となってしまうのだ。惜しい。実に惜しい。

URIを構築しているのは、ActiveResource::Baseクラスに定義されているelement_pathおよびcollection_pathという2つの特異メソッドである。例えばelement_pathメソッドのコードを見てみると、非常に残念なコードであることがわかる。

これでは「.」を取り除くことがフォーマッタモジュールの頑張りだけでできないことがわかってもらえるだろう。解決策としては、これら2つのメソッドを置き換えるしかない。具体的には、

というコードを、例えば先ほどと同じように「app/controller/application.rb」ファイルの最後に記述してあげれば良い。これにより、適用されたフォーマッタモジュールのextensionメソッドがnilを返したときは、拡張子も「.」もないURIが発行されるようになる。めでたしめでたし。

もし「.xmlとか.json付けられないのにActiveResourceでアクセスしなきゃならねーんだよ」的なことになったら、上記のコードを利用してみて欲しい。

map.resourcesでcollectionを指定すると.xmlを受け付けない、なんてことはなかった

RESTful APIをRuby on Railsで実装するための重要な機構に、map.resourcesがある。これはconfig/routes.rbファイルで記述され、URIに関してRESTfulなプロトコルに対応した処理のマッピングを自動的に設定してくれる仕組みだ。例えば、

としてあげれば、「GET /articles/1.xml」というリクエストに対してArticleController#showメソッドが、「DELETE /articles/1.xml」というリクエストに対してArticleController#destroyメソッドが呼び出される、といった具合になる。たった1文でこんな設定が実現できるのは、非常に嬉しい。

さて、上記の例では、URIの最後に「.xml」という拡張子をつけている。これは「レスポンスはXML形式で返してね」という指示を意味する。URIに拡張子を付けなくても、リクエストヘッダのAcceptフィールドでレスポンスの形式を指定することもできる。つまり、上記の設定は、「GET /articles/1」でも問題なくArticleController#showメソッドが呼び出される。

Railsで想定されるRESTful APIは、上記のような「/リソース名/ID」という指定が基本となる。しかし、例えば「GET /articles/rss」というように、ID値指定ではなく、意味のある単語でURIを構成したいこともあるだろう。そのような場合には、map.resourcesでの記述は、

というようにすれば良い。これにより、「GET /article/rss」というリクエストに応じて、ArticleController#rssメソッドが呼び出されるようになる。

しかしRails 1.2.6では、:collectionを指定した際に、「GET /article/rss.xml」というように拡張子を付与した形でリクエストを投げても、認識されずに404となってしまう。もちろん「GET /article/rss」でAcceptリクエストヘッダを指定すれば、レスポンスの形式を指定した上でArticleController#rssメソッドがちゃんと呼ばれる。しかし、「GET /article/rss.xml」とか「GET /article/rss.json」とか「GET /article/rss.atom」とか指定しても、404。

これはさすがにバグな予感がするので、Rails Tracにチケットを発行して登録してみた。

Ticket #10960 「:collection of map.resources doesn’t recognize the extension of URI」- Rails Trac

この問題、別にAcceptリクエストヘッダを使えばいいだけの話なのだが、現状のActiveResourceは、内部で生成されるURIに必ず「.xml」や「.json」などの拡張子が自動的に付いてしまうため、「GET /article/rss」を呼べないのだ。つまり、Railsで作ったRESTful APIなのに、Railsに含まれているActiveResourceでアクセスできない、ということになってしまう。これは余りにもダサい。。。

Rails 2.0でどうなのかは試してないので、もしかしたら上記の問題は発生しない、かも。あとで試してみることにする。

追記:2008年1月31日

どうも僕のmap.resourcesの書き方が間違っていたみたいで、シンプルなアプリを作って検証したら、Rails1.2.6でもRails 2.0.2でもちゃんと拡張子付きで処理された。よって、上記チケットもclose。自分の確認力のなさに絶望。。。orz

不足してるんだって

チミに足りないものカード
よういちろうの足りないものカード
by ふりーむ! 無料ゲーム/フリーゲーム
最近はそうでもないぞ!?
チミの2008年カード
よういちろうの2008年カード
by ふりーむ! 無料ゲーム/フリーゲーム
これは当たってる。

JRuby-1.0.3のRegexp#escapeメソッドは文字コードを指定できない

JRubyの現在の正式な最新バージョンは1.0.3である。しかし、1.1RC1も登場しているので、1.1の正式版が登場するのは時間の問題だろう。安定版ということで1.0.3を使いたくなるし、多くの場合は1.0.3で事足りるだろう。しかし、今日僕は1.0.3で小一時間ハマってしまった。

そろそろこみゅすけの携帯版を作ろうと思い、jpmobileというプラグインをRails 2.0にインストールしていろいろと遊んでみることを始めようとした。もちろんJRuby-1.0.3のgemでRails 2.0.2をインストールし、railsコマンドでプロジェクトファイル一式を生成、そしてscript/plugin installでjpmobileをインストールして、script/serverでいつものようにWEBrickを起動しようとした。

しかし、悲しいことに、エラーメッセージが表示されてしまい、WEBrickは起動しない。

=> Booting WEBrick…
/Applications/java/jruby/jruby-1.0.3/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:499:in `require’: wrong number of arguments(2 for 1) (ArgumentError)
  from /Applications/java/jruby/jruby-1.0.3/lib/ruby/gems/1.8/gems/rails-2.0.2/lib/commands/server.rb:39
  from /Applications/java/jruby/jruby-1.0.3/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require’
  from /Applications/java/jruby/jruby-1.0.3/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require’
  from :1

調べてみると、vendor/plugins/jpmobile/lib/jpmobile/emoticon/z_combine.rbファイルの中でエラーが発生していることがわかった。具体的には、以下の行で引数の個数が合わない、というエラーが発生していた。

この行のどこがエラーになっているのか、script/consoleを使って確かめてみる。すると、

というように、エラーを再現させることに成功。なんと、Regexpクラスのescapeメソッドが引数を2つ受け付けない、ということが判明した。確かに、JRubyのソースコードを追っていったところ、RubyRegexp.javaファイルにおいて、

という記述しか見つからず、文字列一つを受け取るescapeメソッドしか定義されていない。

ここで、Regexpクラスのescapeメソッドは、quoteメソッドと等価であることに気がつく。では、quoteメソッドも同じなのか?

一瞬「お、quoteメソッドは大丈夫じゃん」と思ったのもつかの間、なんとUnicodeしかサポートしないよ、と警告されてしまった。これじゃあ第2引数を渡しても、実質意味がない。

まとめると、JRuby-1.0.3では、

  • Regexp#escapeメソッドでは、渡した正規表現の文字コードを指定できない。
  • Regexp#quoteメソッドを使用して文字コードを指定しても、そもそもUnicodeしかサポートされない。

ということであり、自分でRubyRegexp.javaファイルをいじるなりしなければ「GAME OVER」という結論になってしまった。このメソッドが使用される局面は、普通のWebアプリケーションではそんなにないだろうけど、少なくともjpmobileを使った携帯サイトは、JRuby-1.0.3では構築できない。当然だけど、Rails 1.2.6でもNGであり、CRubyの1.8.6ではちゃんと動作する。

ちょっと悔しいので、JRuby-1.1RC1でもダメなのか?という検証を行ってみた。結論としては、上記の問題は解決されていて、Regexp#escapeメソッドとRegexp#quoteメソッドの両方とも、第2引数をちゃんと受け取るし、指定した文字コードが正しく処理に反映されているようだ。JRuby-1.1RC1、Rails 2.0.2、そしてjpmobileのtrunkという組み合わせで、ちゃんとWEBrickも起動するし、「mobile_filter :hankaku => true」として半角カナ変換も機能している。絵文字は試してないけど、たぶん大丈夫だろう。

というわけで、JRuby-1.0.3は、文字コードの扱いが非常に惜しいレベルであることが判明してしまった。が、JRubyコミュニティも1.1シリーズに今後力を注いでいくだろうから、もしJRubyでRailsするなら、すでに1.1系を使っていくことが現実解だということなのだろう。

いい米

いい米

流行には乗っておこう

いい嫁

今だからこそ、もう一度やって欲しい

SKINOW、復活しないかな。。。

メッセージ指向なオブジェクト指向でのUMLって?

オブジェクト指向は単なるオブジェクト間のメッセージのやり取りに過ぎない、という言葉は、僕の身近な人たちにとっては僕から一度は聞いたことがあるだろう。そんな考えに関して注目エントリが下記。書いてある内容はほぼ同意なんだけど、部分的に引っかかりを感じざるを得ないので、僕の意見をエントリしてみる。
イマドキのオブジェクト指向」- アークランプ より:

では、「イマドキのオブジェクト指向」とは何か。それはメッセージ指向という解釈です。オブジェクト指向は「メッセージによる処理の分割」であり、「分離された処理をオブジェクトと呼ぶ」と定義します。これまではオブジェクト指向とは「オブジェクトによる処理の分離」であったわけです。

太古の昔から、オブジェクトは「メッセージのやり取りによる処理の委譲の繰り返し」だったはず。「継承ではなく委譲を使いましょう」なんて思想は、オブジェクト指向の基本であり、それはメッセージのやり取り。そもそも、オブジェクト指向って聞いてメッセージのやり取りが真っ先に思いつかない人間は、過去も現在も未来も、オブジェクト指向についての理解がズレているということになる。
特に、上記のような「メッセージのやり取り」という概念がない or 薄い捉え方をしてしまう原因は、メッセージのやり取りであることを素直に取り入れている言語が少ない、もしくは取り入れられている言語が今まで普及してこなかった、ということに起因すると考えられる。つまり、JavaやC++など、構造化言語から派生した言語を習得することによってオブジェクト指向を学んでしまった人間にとっては、メッセージのやり取りという発想ではなく、あくまで「関数(=メソッド)を呼び出す」という発想しかない。つまり、他のオブジェクトに対しての処理の委譲について、基本的に完全な同期処理しか体験できないために、メッセージングの手法が限定され、結果としてメッセージングという考えに行き着かない、ということなんだろう。
別にSOAとかのアーキテクチャからオブジェクト指向のメッセージングを見い出すのではなく、Rubyなどの「メソッド呼び出しをメッセージとして捉えることができるオブジェクト指向言語」をちゃんと経験することによって、本来のオブジェクト指向の考え方を体験して欲しい、と僕なんかは考えるわけである。

 ここで振り返ると、現在のシステムで重要なのは「オブジェクトのデザイン」よりも「関係性のデザイン」であることが分かるのではないでしょうか。もちろんオブジェクトのデザインは変わらず重要です。ですが、関係性のデザインをうまく使うことで、よりシステム開発が進歩する気がします。

 その点でオブジェクト指向という名称は視野を狭くしてしまうのです。UMLも関係性のデザインについては非常に弱い。

UMLの話は、真逆だと思う。純粋にオブジェクト指向の領域において、UMLは他のどのコンピュータ言語よりも、素直にメッセージングに関する記述ができるはず。単にクラス図だけを取り出して話をするのであれば、確かに静的関連しか情報は得られない。しかし、UMLの各図の仕様は、メッセージングについて同期であれ非同期であれ、更には処理の待ち合わせであったり、オブジェクト間の動的な関連(=ランタイム時のメッセージのやり取りから生じる関係性)なんかも、ちゃんと記述することが可能である。
更に、オブジェクト指向においてのメッセージのやり取りの具体的な送受信実装手法を切り離して、純粋なオブジェクト間の動的なメッセージのやり取り(=本来のオブジェクト指向設計)は、UMLが得意とする部分である。もっとUMLが、SOAなどのアーキテクチャやRubyなどのオブジェクト指向をちゃんと取り入れている言語での開発について、多く使われてもいいのではないか、と思うのだが、なぜかUMLは敬遠される。勝手な想像をするに、それは偽オブジェクト指向言語(=構造化とオブジェクト指向の中間、的な)においてUMLを適用しようとして体験したGAPの印象があるから、という理由に思えてならないのが、実際のところどんなんだろうか?

なぜソースコードをオープンにすることに抵抗がないのか

僕は、自分が作ったソフトウェアのソースコードを公開することについて、ほとんど抵抗がない。一般的には、バイナリコードのみを公開すれば、利用者は満足するはずである。にも関わらず、ソースコードを公開することに関して、「別に当然じゃん」的なことを思ったりする。何故なのか?
PCというものに出会ったのは、小学校高学年。それ以来、僕はPCというものが手元にずっとある状態だった。もちろん昔はインターネットなどがなかった時代。ソフトウェアは、店頭にならんでいるもの、という認識が一般的だった。
しかし、僕が小学校に行ってたとき、つまり80年代から、僕は「ソースコードが公開された状態のソフトウェア」が数多く存在する環境で育った。そう、雑誌である。雑誌の中でも、「マイコンBASICマガジン」や「I/O」など、読者が投稿したプログラムが数多く掲載されている雑誌を好んで読んだ。これらは月刊だったので、1冊につき20本以上掲載されたソースコードについて、毎月目を通していたということになる。年にすると、最低でも20×12=240本のソースコードを読んでいる計算。
1本1本は誰が作ったかもわからない、レベルも様々なもの。僕は「ファミリーベーシック」「FM-77」「PC88MH」という経路を辿ってきたが、それこそ自分が持っていない機種のソースコードまで、脳内で実行していた。機種依存のわからない命令は、本屋に行ってその機種を解説している本を見つけ、立ち読みしたり購入したりして知識を手に入れる。そんな毎日だった。
つまり、今でいうオープンソースの中で、僕は育ってきたのだ。「いつかは雑誌に掲載されるようなプログラムを作るんだ」というモチベーションが、当時の僕をPCに向かわせていたと振り返ることができる。ソースコードが公開される、という素晴らしい結果に憧れていたんだ。
もちろん、店頭に並んでいるゲームやワープロなどといった、いっぱい売れて広く認知されるバイナリコードを生み出すことにも憧れはあった。でも、特に売れたゲームなどは、その高い技術が「内部解説本」としてユーザの手に渡ることがあった。その解説本には、もちろんそのゲームで使われている技術が、ソースコードの状態で掲載されていた。つまり、店頭に並んでいたバイナリコードも、そのソースコードの一部はオープンにされ、僕はそれを見て高い技術に触れてきた(当時理解できていたかどうかはともかく)、ということだ。
結局、昔も今も、個人や企業が作ったソフトウェアはソースコードが公開され、技術者が目にすることができる、という状況にかわりはないという感覚を僕は持っている。そして、自分で書いたソースコードをオープンソースにできるということは、自分にとってこの上ない喜びなのだ。
ちょっと極端な感じの内容になってしまったが、最近なぜか学生当時のプログラミングに対する気持ちがよみがえってくることが多かったので、電波的な話をエントリしてみた。
どうなんだろう、今となっては、↑は「年寄りの昔話」なのだろうか。。。

Twitterclipse1.3.2をリリースしました

しばらくEclipseから遠ざかっていたので、Twitterclipseも自分で使っていなかった。今日やってみたら、truncated属性がAPIに追加されていたみたいで、投稿できてもステータスが見れない状態だった。
上記不具合を修正し、1.3.2としてリリースを行った。もし興味のある方はバージョンアップをして欲しい。

Get Adobe Flash playerPlugin by wpburn.com wordpress themes