6 月 11 日 午後の部 - 2
Ruby on Rails 関連の三本立てです。
David の基調講演は英語で行われました。
デンマーク人が日本人の作ったプログラミング言語で構築したアプリケーションを本人が英語で講演したものを日本人が日本語に訳したような回りくどい (『Rubyを256倍使うための本 邪道編』p. 107 参照) 状況で、内容を十全にお伝えできませんことを、あらかじめ御了承ください。
6月11日 14:30〜
moriq (吉田和弘)「Ruby on Rails 開発事例」
「Rails による Web アプリケーション開発・保守事例の紹介」
- スピーカー
- moriq (吉田和弘) ((株) ミッタシステム) さぬきうどんで有名な香川県。Apollo の作者。
- 時間
- 14:30〜14:59
- セッション概要
- Rails によるアジャイルな開発・テスト、リモート保守まで
- 講演資料
- http://www.moriq.com/ruby/RubyKaigi2006-moriq.pdf
- 自著宣伝度 ★★
- 場数度 ★★★
- うどんらぐ度 ★
- 中国四国地方の地理案内その (2) 度 ★★★
出会い
昔 Perl で書いた CGI、仕様拡張。どうしよう……というところで、Ruby で書き直した。
MySQL/Ruby 書きやすかったんですけど、大きくなると保守たいへん。
CGIKit
しっくりこなかった。テンプレートエンジンの存在が慣れず。
Ruby on Rails
すぐにしっくりきた。
- 前から ERB つかってた。
- 使いやすい形でまとめられていた。
Rubyist Magazine で紹介
最近は編集に回る。書いてても反応薄いんですけど。書き手募集中。
「ライド・オン・Rails」
今月末出版されます。babie さんと一緒に書きました。
開発事例
など。
あんまり実装の話はでてこない。
託児施設 (かずひこさんとゆうなさんのお子さんの写真を使用)
Apollo で GUI のテストをある程度自動化できた。
お子さんは 4 人まで登録できるので、まつもとさんちも大丈夫。
ActiveRecord
テストを自動化できた。
- 月次請求書
- Win32OLE、Apollo→Excel
…でもフォーマット凝った人がいて。
締め切りが近いことを思い出すとつらいんで、この話はこのへんで。
リモートメンテナンス。
テストちゃんと書いてたら、がんがんアップデートしてよい。
ほんとは Capistrano 使いたいんですけど、コマンドラインで間に合ってる。
データセンターは松山に設置。どこにあるかわかりますかね?
Rails はどこに
これまでの話、Rails ぜんぜん使ってないじゃん! 運用構成図上は出てこない。
LAN なので、アイコンのダブルクリックで svn update が始まるようにしている。
大学附属病院だとかで、LAN でつながってないのでダイヤルアップで接続しなければならないところがある。これも起動時に…外部回線を通るので、適当に SSL つないで暗号化して通す。
PTAの連絡会 携帯メールへの対応
has_and_belong_to_many (HABTM) を使う。
見た目は単純。
User / Room / Topic と、それら二つの間の関係とで、HABTM×3。
交叉テーブル上のカラム? 直接 SQL 書かないと
文字列を URL として表現するには、数値に直す必要あり (base64 を使用)。
Room と Topic を決めたときに、誰が登録? 誰に発行? SQL 書くしか!
has_many:through、よさげ。でも更新が書きにくい!
WEB+DB PRESS 最新号 (Vol.33) を、買ってね (はぁと) 預言書いてます。
ほとんどの携帯では Cookie 使えないので Trans SID とか使う。
URL パラメータは人の見えないようにスクランブル (可逆に構成) する。
強い方法ではないので有効期限設定。じつはページキャッシュ使える、かも。
ActionMailer 最初は使ってたけど、中で使っている TMail が、ケータイでよく出てくるような妙なアドレスに対応してくれない様子。
なので、TMail ごと捨ててなんちゃってライブラリを自作した。
歴史的理由で MySQL を文字コード Shift_JIS のデータに使っていたが、そうすると 「\」の罠や quote の罠にはまって文字が化ける。→やはり文字コードは UTF-8 に統一がよい。
Q&A
- Q1 (Yugui さん)
- Apollo 多用したのは?
- A1
- GUI が必要になったときに、一番慣れているから。Windows でしか使えない弱点は認識している。
- Q2 (卜部さん)
- blowfish (base64)、なんで base64?
- A2
- 単純に URL に直したくて。
- Q2 (卜部さん)
- %xx でやれば?
- A2
- 16 進数なので長くなる。できるだけ短くエンコードしたい。
- Q3 (野村さん)
- 携帯と、そうじゃないときに、ビューだけ変えたいけど、定石あるのか?
- A3
- コントローラから (ケータイとそれ以外を) 分けてます。画面遷移の方法から違うので。
- C3 (DHH さん)
- そういう質問は、わたしのときにどうぞ。
西和則「ActiveRecord を詳しく」
- スピーカー
- 西和則 (aka Maiha / aka ヽ( ・∀・)ノくまくまー)
- 時間
- 15:00〜15:29
- セッション概要
- 強力な ORM ツール ActiveRecord の紹介
- 講演資料
- http://wota.jp/rk2006/ (参照用), http://wota.jp/rk2006.tar.gz (お持ち帰り用)
- ソースコード全開度 ★★★
- 「David にはがっかりだよ」ウケ度 ★★
- 持ち時間の半分はクゥ〜ン♪でできていました度
- 優しさの半分は 从*’w’) でできています度 ★★★
意識調査
- 「優しい Rails の育て方」読んだことのある方? (お客さんの 100 人強が挙手。最前列の DHH は手を挙げず。見て) おおお。
- 「David にはがっかりだよ!」
ActiveRecord
- 以下三階層の皆さんに均等にお話する。() の中がページ数です。
for Ruby users (4)
これからずっとソースばっかりです。
CRUD: ActiveRecoed Pattern
- ふつう require_gem してください。
- Model クラスを ActiveRecord::Base から継承するだけで簡単に ORM できる。
- 最初からクラスとして呼ぶだけで、DB 意識せずに使えるのが一番ビックリ! おまじない不要!
- これはデータベースとのコネクションが ActiveRecord::Base のクラスメソッドから提供されているため。
- 保存したいオブジェクトをデータベースと紐付けるために面倒な手続きをしなくてよい。
- データベースコネクションを表すオブジェクトに対して操作をするのでなく、アプリケーションにおけるモデルを表すオブジェクトそのものに対して自然に CRUD 操作可能。
- 簡単なのでこんな使いかたもできる。
- Syslog をモデル化して、メソッドの rescue 節でとにかくエラーを拾って Syslog.create しておく。
- 例外ログが全部 DB に入るので管理し易い。
- (Syslog に level という attr があるとすれば、Dynamic finder という機能で) Syslog.find_by_level(FATAL) なんていうクエリーが書けるので検索も容易でいろいろ楽しい。
Association (関連)
- has_many (一対多) 操作は find magic。
- ActiveRecord 奥が深い。
- これで Ruby のみなさんとはお別れです。
for Rails users (15)
with_scope
- 使ってる人、手をあげて。(会場を見渡して) 3 人。
- Google で検索しても 15 件でした。
- いまどき Google で 15 件て typo しかありえない!
with_scope を使うと、データがマスクされて見える。
- with_scope メソッドに検索条件を渡してブロック付きで呼び出すと、そのブロックの中の検索や更新はすべて with_scope に渡した検索条件にマッチするもののみに作用する。
- 条件を絞って検索、が、find(:all) でいい!
- RDB でいうところの「ビュー」のように使える。
検索パラメータと with_scope で隠れている部分が衝突したら?
- with_scope の内容を優先する。不正アクセス防止に使える!?
- Rails アプリケーションへの入力は ActionController::Base#params メソッドを通じて取得する。
- 理屈の上ではそのまま DB クエリの条件として利用できるが、普通は妙な値を入れて攻撃されることを考えて値を加工しなければならない。
- セキュリティ的に触られたくない値に with_scope でデフォルト値をあたえてやれば不正アクセス防止になるかもしれない。
- でも、Controller の各アクションメソッド内でいちいち with_scope するのは DRY でない。
scoped_access プラグイン
- コントローラのアクションで with_scope の条件に相当するものを宣言的に割り当てられる。
- こんなふうにしたくなるのがプログラマ。
scoped_access の仕組み
- 注意: ActiveRecord.allow_concurrency = true にしないとスレッドの同期ではまる。デフォルトでは true→false になっている。
- 一部の皆様、ご静聴ありがとうございました
for Rails developers (17)
Acts_as_view
- View は良いものだけれど、DBMS への依存性が強い。書き戻しができなかったり、そもそも View がなかったり。
- そこで、Single Table Inheritance を使う。
- Single Table Inheritance (STI) 使っている人…けっこういる (10 人強)
- Member というモデルに対して、ActiveMember, DeletedMember というサブクラスを作って STI。
- insert のみする形に変えれば (削除するには DeletedMember を insert する) update, delete が出来なくても無問題?
- しかし、Rails の STI は “type” カラムに依存。複数カラムを扱えない。
- それなら with_scope か? 複数カラムもいけるはず。ネストもできるので継承もできる
- STI かける。”type” を name にするだけ。でも一つだけ問題。Person.find するとCostomer object が出来る。
どうせ Ruby なら動的に view を作りたい!
- 無名クラス? → モデルの scope 空間を汚染しない!
- インデクサメソッドがほしくなった。作った。
- Member.find(:all, condition) ではなく Member[:all, condition] と書きたい
- 更に、Member[:key => value]で、with_scope(:conditions => [‘key = ?’, value]) で絞ったのと同様の動的Viewが作れたりできたらうれしい。
- Member[:key => value][:all, condition] とか、やっていくと暗号になりそう。
- 暗号になりかけのところでやめたらいい感じ?
- Changeset 4425 出たな。じゃ :conditions にハッシュを?
- Member.find(:first, :conditions => [‘key = ?’, value]) の代わりに Member.find(:first, :key => value) と書ける。
Finder Query
- SELECT を実行してしまうのではなく、単にクエリ文字列を取得するメソッド (finder_query) が欲しい。
- 従来、サブクエリが必要な複雑な SQL は、結局 SQL をベタ書きしてfind_by_sql を使っていた
- サブクエリ部分の構築を finder_query に任せればあとは普通に find するだけで済む。
- ぜったいほしい!
基調講演: David Heinemeier Hansson「One controller, many ins, many outs」
or: Discovering a world of Resources on rails
- スピーカー
- David Heinemeier Hansson
- 時間
- 15:30〜16:30
- セッション概要
- Why don’t we unify web application services…
- 基調度 ★★★
- 一貫哲学度 ★★★★
- 会いたかったよ、David 君。度 ★★★
- Mac freak 度 (one more thing とか) ★
- 日本語度 -
- ヒゲ度 -
- 頭とんがってる度 ★
Thanks
- REST
- HABTM での REST
- has_many :through での REST
- ActiveResource
New directions?
Recounting
Rails の成長はまだ middle に至っていない。
In the beginning….
- MATRIX JOURNAL / Ruby (上記 Linux Journal の表紙を映画「マトリクス」風に改変したバージョン)
- ここにいるみんなは赤いカプセルを飲むクチだと思う (「マトリクス」では主人公が赤い錠剤を飲むことで、楽しいが偽の現実より苦しいが本物の現実を選ぶ)。
CRUD
- もったいないとか,カンタンすぎるとか,本物の開発者は使わないとか。
- いかがわしい? 恥ずかしい? そんなことはない!
私は如何にして心配するのをやめて CRUD を愛するようになったか
- SQL は CRUD。Ruby のメソッドでも CRUD。実はもう一層ある! http が。
- 従来の http 層の書き方が DRY でなかったりしないか。
- たとえば GET people/show/1 といった書き方を見る。しかし show したいから GET するのではないか?
- DRY にしたければ GET /people/1 でなければいけない。
- PUT や DELETE も 使ったら、HTTP メソッドだけで CRUD になる。
HTTP |
メソッド |
POST |
GET |
PUT |
DELETE |
コントローラ |
アクション |
create |
show |
update |
destroy |
SQL |
文 |
INSERT |
SELECT |
UPDATE |
DELETE |
- /people/show/1, /people/create/1, /people/update/1, /people/destroy/1
- こういうのは全部 /people/1 という URL にまとめてしまう。
- アクションは HTTP メソッドで表す。map.connect ‘:controller/:id’ と定義しておく。
HTTP の語彙の射影…… 問題
- 今ある HTML ブラウザの form では PUT や DELETE が使えないのだ!
- でもそれは単にクライアントの問題。
- 今でも GET や POST は簡単に出来るので、Rails 上では CRUD に対応させておいて、見分けがつくように実装しておく。
PUT や DELETE が使えないうちは、form の hidden フィールドや JavaScript を使って GET や POST に逃げる。あとでブラウザが全部対応するようになっても、ユーザコードの変更は必要ない。
これでよくね?
- 一貫性? 簡単さ? 見つけやすさ? どれも大丈夫。でも……
「これしか出来ないのは不自由なのでは?」でも、それは違う。
制約が自由をもたらす (Constraints are liberating)
思考を縛る拘束衣 (straight jacket!) (CRUD という拘束) のおかげで、大事なことから目を逸らさずに済むことがある。予めの制約の中で自由に行動すれば、それには必ず正しさの裏打ちがある。
―― 記者の余談ですが、「從心所欲不踰矩」(ココロのホッするトコロにシタガいてノリをコえず; 論語 為政 四) という言葉を思い起こさせます。
CRUD の視点でシステムを見直す。
- Rails 1.0 までで自分はみんなを間違った方向に導こうとしていたかもしれない。
- Backpack のコントローラには、15 個もアクションがあったりする
多対多 (has_and_belong_to_many) その 1
もっと簡単な例を示す。先の People が集まって、Group を作るとする。
- Group は People 改め User を add_ したり remove_ したりする。→GroupControllerに記述??
- User は Group に join_ したり leave_ したりする。→UserControllerに記述??
なにかおかしい。Group の CRUD 以外に User 操作が混じっている。
Rails 1.1 での解は: Group と User とを、Membership という新規のモデルでつなぐ。
そして Membership の CRUD を考える (関係を作る、解消する……) ことで解決。
多対多その 2: 会員制サービス
- 自分の顧客に会員制サービスを提供するようなケース。
- 会員は、めいめい提供されているプランを選ぶわけだが、ここで「お得意様」向けプランだけのサービスをどうやって提供するか。
- アカウントは会員のものだが、プランはそうではない。
- アカウントに、どのプランが選べるのか考えさせるのは、会員のアカウントの CRUD とは関係ない。何かが足りないのではないか。
解は: Account と Plan の間に Subscription というモデルを導入する。
プランの選択や解除は Subscription の CRUD になる。また、関係を付けていいかどうか (お得意様?) の判定を組み込む。
関係がつくかどうかは、関係自体が責任を持てば済む話だ。
モデルは‘モノ’であるばかりではない。
いまのように関係であるかもしれない。イベントであるかもしれない。状態でさえあるかもしれない。
CRUD は天使のたくらみ
こういう見落としがちなモデルを CRUD の手助けで見つけることが出来る。CRUD こそは僕の天使さ。
プログラマの右耳に悪魔がつき、左耳には天使がつく。
悪魔のささやき「コントローラにメソッド追加すればいいじゃん」がスパゲッティコードを生むんだ。
天使の力が強かったら、こんな言葉に惑わされはしない。そしてイヤなことが起きそうなコードがすぐに分かるようになる。CRUD の歌を歌おう。
CRUD だけでは生きていけない
- CRUD はゴールじゃない。ゴールを目指す気持ち (aspiration) なのだ。
- 強力だけれど、設計の技法であるに過ぎない。
Kase < ActiveRecoed::Base
Kase は close というアクションを持つ。あるケースを close すると、それは完了して、それ以上処理されない。
問題は、完了処理は属性一つ書き換えるきりでは済まないことが多々ある、ということだ。カプセル化が効かないことがある。そんなとき設計指針の CRUD がそのまま通用するわけじゃない。‘ある一面’からだけ、物事を処理したくなるときがある。
で、そんなときは _ ; _ を使う。
どうだったら辛くないのか、によるが、CRUD で実装してみることにする。
イベントを表すために Closure というモデルを導入し、誰が close したのかの情報を別に持つことにする。さらに状態を表すために Progress というモデルを導入して、いわゆるステートパターンを構成する。これで CRUD は守りきれる……が、これはさすがにやりすぎ。
これは構成 (constitution) の問題であり、バランスを崩してまで CRUD に走ることはない。
入力いろいろ。出力もいろいろ。でもコントローラは一つきり
それなら何も強力に CRUD ばかり追い求めることはないのではないか?
(Web サービスで) MIME タイプという便利なものと CRUD を組み合わせると、実はとてつもなく強力なのだ。
mime type に応答する (HTML なのか XML なのか、はたまた JavaScript)
MIME タイプを利用して、一つのコントローラで複数のクライアントに対応したり複数の結果を戻したりできる。
特定のファイルタイプを指定して戻すとき
respond_to メソッドで振り分ける。
- Accept ヘッダフィールドを利用して推測する方法
- ヘッダフィールドの他に拡張子でもいい。両方あれば、拡張子を優先。デフォルトでは HTML を出力すればいい。
CRUD との組み合わせで、ひとつのコントローラで何が来ても対応できるのは cool。
出力だけじゃない。入力でもいろいろな MIME タイプを受け付ける。
こんどは Content-Type ヘッダを見て対応する。あるいは拡張子。
デフォルトは application/x-www-form-urlencoded。
入力を XML 構造で受け付けることもできる。入力形式の変更には Content-Type ヘッダを使う。
まだ存在しないコンテンツタイプには、どう対応するのか?
たとえばケータイ。入出力の定義に、なんちゃって MIME タイプ ‘text/x-mobile’ を、before_filter で UA や IP で判断して追加する。
あとひとつ: ActiveResource のアイディア
思いついたばかり。
ここまでで、Rails の CRUD やインターフェースについて説明する中で、今の Rails に一貫した規約があることが分かったと思う。
規約があれば、それは自動化できる。それなら、Web サービスも自動化しよう。
ActiveResource は、ActiveRecord から CRUD を操作できるように Web サービスを外部から操作できるようにするための新しいフレームワークである。
最初にサービスの URI と認証の情報が必要だが、それらをモデルの中に隠蔽してしまって後は気にせず、(REST の) API でモデル操作を公開する。
事例は今まで挙げたものと同じ内容。コード上は今の ActiveRecord と同じ使い方ができる…ようにしたい。今のところ、ActiveResource は単なる設計方針でしかなく、何も実装されていない。
ネットワークから要求が帰ってこなくなるような状況に対応するために、credentials つきの認証を Web でやりたい。
まとめ
Rails 1.2 でできる予定のこと:
- Web サービスがかんたんに構築でき、利用できるようにする
- 一つのコントローラで、いろいろな (PDF や Excel などの) 出力ができるようにする
Q&A
- Q1 (stoyan さん)
- PUT DELETE は WebDAV を使えば今でも利用できる。Rails で対応すればハッピーだけど、対応する予定はあるか?
- A1
- すごい面白いけど、自分じゃ使わない。興味がないので。いい機能だと思うから誰か作ってくれるといいね。
- Q2 (kstn さん)
- モバイル対応の例。Accept や拡張子だけじゃなくて、同じ URL で User-Agent を見て切り替えられないか。
- A2
- フィルタでできる。before_filterでいろいろなルールを設定する Accept ヘッダを書き換えれば良い。
- Q3 (乃村さん)
- 最後のほうの respond_to feature。本 (二度) 読んだけど知らなかった。いつから使えるのか?
- A3
- 1.2 待っててね。そのあと書くアレの第二版には載るでしょう。
- Q4 (安藤さん)
- ActiveResource。次のバージョンで入るのは、Web サービス本体が Rails で書いていないと、うまくつながらないような。
- A4
- こんどのは、確かにそう。その次の実装は、ちょっと変える。そもそも REST だけしか対応してないので…
- Q5 (drawnboy さん)
- ActiveResource。外部にあるリソースを扱うので認証が必要だと思うが、いま使われてるのがけっこうバラバラ。フレームワークで統一的にはできないのか?
- A5
- まずは https だけ。順次 http digest とか SSL とか追加していこうかな。
- Q6 (高橋 (征) さん)
- ActiveResource。XML-RPC 版とか、SOAP とかではやる予定ないの?
- A6
- きっとできるんだろうけど、SOAP や XML-RPC は好きじゃないので、自分じゃやりたくない。(笑)