Dynamic Finder

Ruby On Rails の Dynamic Finderの仕組みが不思議です。

BOOKSテーブルに対応するモデルBookクラスに対して

Book.find_by_isbn('1234')

とかやると、BOOKSテーブルから値が返されます。

select * from BOOKS where isbn = '1234'

と等価です。

一方、Bookクラスの実装はというと

class Book < ActiveRecord::Base
end

なわけです。find_by_isbn なんてメソッド定義がありません。

Javaの場合、少なくともインタフェース定義しないとメソッドは呼べません。

どうやってるんでしょうか。

Rubyの場合、呼び出したメソッドが存在しないと、そのクラスの method_missing というメソッドに処理が委譲されるんですね。

その仕組を利用して、method_missing の中で、find_by_isbn の字句を解析して、SQLを組み立てているようです。
method_missing なんて、動的型付け言語らしい機能です。
こういうのがRubyの面白いところなのかな。

ActiveRecord3.0.9のbase.rbの中を見て見ました。

def method_missing(method_id, *arguments, &block)
  if match = DynamicFinderMatch.match(method_id)
    attribute_names = match.attribute_names
    super unless all_attributes_exists?(attribute_names)
    if match.finder?
      options = arguments.extract_options!
      relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped
      relation.send :find_by_attributes, match, attribute_names, *arguments

このコードを読み解く力はまだ私にはありません。
finderの命名規約にマッチした場合に、finder処理を組み立てているんだ、というのはなんとなく読み取れます。

ちなみに、Book.find_by_isbnのBOOKSテーブルとisbnカラムがDBに存在することはいつチェックするんですかね?Railsの中でDB定義のコンフィグとかは持っていないように見えるから、とりあえずSQLを投げてしまうのだろうか?あるいは、DBのメタデータにアクセスする?
上のコードを読み進めていけばそのうちわかるのでしょう。がんばります。

それにしても、変数の型がすぐにわからないから、どうやってコードを追っていけばいいのか、まだよくわかってない。一つ一つクラス定義を探しながらやっていくしかないんだよね。こういうところはJavaより少し面倒だ。

ビューヘルパー form_for

RESTfulな使い方では、一つのフォームパーツを登録と編集で使いまわすことができるのですね。例えば、以下のフォーム記述。
form_for で作ったフォームは、引数にとるモデル@bookの内容によって、送り方が変わる。
@bookが空の場合は、 action="/books", HTTPメソッドがPOST
空でない場合は、action="/books/1"、HTTPメソッドがPUT(疑似)
となる。

"/books" 、HTTPメソッドがPOSTに対しては、createアクションが呼ばれる。
"/books/1" 、HTTPメソッドがPUTに対しては、updateアクションが呼ばれる。

これで、formを共通化しても、登録と編集とで使えるわけね。

<%= form_for(@book) do |f| %>
  <% if @book.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2>

      <ul>
      <% @book.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label 'ISBNコード' %><br />
    <%= f.text_field :isbn %>
  </div>
  <div class="field">
    <%= f.label 'タイトル' %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label '値段' %><br />
    <%= f.text_field :price %>
  </div>
  <div class="field">
    <%= f.label '出版社' %><br />
    <%= f.text_field :publish %>
  </div>
  <div class="field">
    <%= f.label '発行日' %><br />
    <%= f.date_select :published %>
  </div>
  <div class="field">
    <%= f.label :cd %><br />
    <%= f.check_box :cd %>
  </div>
  <div class="actions">
    <%= f.submit '登録' %>
  </div>
<% end %>

RESTfulインタフェースと非RESTfulインタフェース

  resources :books

これで、リクエストURLが直接、booksテーブルの操作につながる(RESTful)わけですが、カスタムな操作を加えたいときは、これがちょっと邪魔です。

例えば、addnumアクションを呼び出したくて、

 /books/addnum

というリクエストをしても、

 /books/:id

とみなされます。RESTfulインタフェースとして、/books/:id はshowアクションを呼び出します。

従って、

 /books/show/addnum

という呼び出しに解釈されています。

気持ち悪いなー、と思ってたんですが、resources ルート定義しなきゃよいのですね。
resources ルート定義をやめて

 match '/books/addnum

あるいは

 match ':controller(/:action(/:id(.:format)))'

として、全ての操作を許容すればよいみたいです。

上の方法だと、アクション一個ずつ定義しなくちゃいけなくて少し面倒。
下の方法は、やりすぎな気がする。

どちらがよく使われるのだろう?

Scaffoldingで生成したコードを写経

Scaffoldingで生成したコードを、再度、自分の手で書いて見ながら、
ちょっとコードを変えたりして、遊んでいます。

Rails では、/books/show/1 というURLをたたくと、
BooksContoller#show にパラメータとして'1'を渡して
respond_to というメソッドによって、show.html.erbに画面が
遷移します。

このとき、遷移元の index.html.erb では、以下のように記述します。

    <%= link_to 'Show', book %>

この結果として、リンク先には /books/1 などのように、URLが生成されます。ここでの1は、bookオブジェクトのidです。

Rails では、/books/1 は、デフォルト(resources :books という宣言がroutes.rb に必要)でshowアクションを呼ぶことを意味します。

従って、BooksController#show としてこの処理を実装するのが望ましいということです。

しかし、この時に、BooksController#kensaku メソッドを呼び出して、kensaku.html.erb の画面に遷移する、という形にすると、リンクの書き方は面倒になります。

    <%= link_to 'Show', ' books/kensaku/' + book.id.to_s%>

こんなこと普通やらないですよね。
レールを逸脱してみたら、面倒になったということです。

実際の業務アプリ実装でも、デフォルトのshow とか edit をやっぱり活用していくんでしょうか。いろんなオペレーションが必要になったりしないのかなぁ。

というわけで、基本的なCRUDがなんとなくできるようになってきているところです。

RubyOnRailsことはじめ

とりあえず意味のあるコードを1日1個あげていくことにしていく。

Ruby On Rails では、Contollerってやつが、StrutsのActionみたいなやつなのね。

app/controller/hoge_controller.rb

# coding: utf-8

class HogeController < ApplicationController
  def index
    render ({:text => 'こんにちは、hogeです'})
  end
end

これでURLに http://localhost:3000/hoge って打てば、メッセージが表示されたよ!

簡単だね。

再開したよ

ちょっとだけ書評を書いてみて、しばらく止まっていたこのブログ。

とあるきっかけで、コードをもっと書いて公開していこうと決意しました。


目標は最終的に何か作品を作ること。

JavaエンジニアがRubyでサービスを作れるようになることを目指します。

Hello Worldから始めますよ!

はじまりはじまり〜

容疑者Xの献身 (文春文庫)作者: 東野圭吾出版社/メーカー: 文藝春秋発売日: 2008/08/05メディア: 文庫購入: 36人 クリック: 219回この商品を含むブログ (684件) を見る

初めて読んだ東野圭吾作品。

トリックは絶妙です。
一応、理数系の端くれである自分も、かつて「美しい数式がある」と感じたことがあります。少しその感覚を思い出しました。

途中までは、少しずつ容疑者Xこと石神のトリックが暴かれるストーリーに、「まあ普通のトリックじゃね?」という心持で読み進めていましたが。いや、きっと石神と湯川の天才二人のやりとり、直線的に話が進むわけがないと、うっすらと感じながら。

後半はノンストップ怒涛の展開。全くだまされましたね。読者、警察ともども、皆、石神の思う壺です。水平思考のなぞかけに引っかかったような感じです。でも、ただ巧妙なだけではない。ちゃんとトリックの行く末に彼にとってのユートピアが待っている。ユートピアという言葉は相応しくないか。彼にとっての理想。石神にとっての美?

ロジック的には、ストーリー的にはすごく締まりがあって、後から思い返して読み返して、ある意味では後味が良い作品だと思いました。

これは人間臭さ抜きの100%ロジカル推理小説としての感想。

ここからは、人間ドラマとしての感想。

否、そんな分けて論じるものでもないのかもしれないですが、トリックの巧妙さと、人間性のあり得なさがあまりに対照的で、すっきりと消化できないのです。むしろ、それこそが石神という男であり、湯川をして「それなりに魅力的な人物」と言わしめた所以か。とにかく、石神という男がわからない。それが、この作品を際立てているのかもしれないが。。。

現実離れしていると思うのは、石神の愛の形です。こういう小説で現実主義をあてはめるのもお門違いなのかもしれませんが、石神の愛には、余りに人間らしさが欠けているように思います。泥臭さが無さ過ぎます。途中、嫉妬したりする場面があったりしますが、僕はそんな石神に安心したのです。でも、最後に向けて、そんな泥臭さはどんどん隠れていく。石神よ、ストイック過ぎます。こんなにも自己愛抜きに他人を愛せるんだろうか?自己犠牲とは少し違う。

花岡母娘が石神の心を救うエピソード、あれはまさに、目の前に神が降臨する体験をしたのではないかとさえ思えます。悟り?そうでないと、この石神の献身は不可解です。もう、この時点で人間の体験を超えてしまったのではないでしょうか。一方の花岡母娘はそれを意図せず、普通の人間であり続けるのです。そこに、石神の不幸を感じざるを得ません。結局、靖子は最後は折れてしまいます。なるべくしてなったのだと思います。石神が愛した靖子は、靖子が自覚している自分ではないのです。靖子に神を見た。それは靖子にとっては真実でも何でもないが、石神にとっては真理なのです。この距離感は一体なんでしょう。愛は崇高であればあるほど、そこから離れた人にとっては辛いものなのでしょうか。

さて、彼らはその後、どうなったのでしょう。

僕の願いは、等身大の石神が、等身大の靖子を愛することです。石神が靖子の入獄に納得するには、靖子の神性を自ら打ち消すしかないのだと思います。きっと、それを認めないと、靖子とも世間ともきっとバランスが取れない。だからこそ、第2の殺人を犯してしまった。

どうかお幸せに。