2007年08月05日 (日)
裸リムーバブル [テクニカル]
「裸リムーバブル」と呼ばれる、SATAのHDDをそのまま(インナートレイとか無しで)着脱できるリムーバブルラックをついに導入してみた。ほんとは買ったのは上のじゃなくて同一品のツクモのEX-SARC1B。玄人志向のと比べて500円ほど高いのでなんか損したかも。
まだSATAのHDDは2台しか持っていないのだが、それでもこれは予想以上に便利だね。今までパラレルATAのときはインナートレイ式のを使っていてトレイをいっぱい買っていたのだが、なんか今までがバカみたいに思えてくる。
まあ裸だと基板が剥き出しなのでそれなりに扱いには気をつけないといけないし、あと予想以上に熱くなるので(フォーマットしたあと取り出したら熱くて火傷しそうになった)注意しなきゃいけないというのもある。
でもやっぱりとても便利なのでなんかちょっとうれしい気分。
参考:
投稿者 4bit : 23:30 | コメント (0) | トラックバック (0)
2006年12月17日 (日)
Rails勉強会@東京 第13回&忘年会 [テクニカル]
前回に引き続き、毎月恒例のRails勉強会に参加。今回は日本Rubyの忘年会も開催。
ポジションペーパーは、「URIにidを使いたくない場合」ということで前のエントリそのまま。
前半セッション「AWDwR 2ndを読む」
最初セッション案があまり出ていなかったので、なんとなく僕が挙げた「Agile Web Development with Rails 2nd Editionを読む」が選択されて、オーナーを務めることになった。
しかしやっぱり全部読めるわけはないんで、読む場所は絞っておかなきゃいけないね~。リクエストのあったActiveRecordとActionController(ルーティングとRESTとか)あたりの改訂箇所を中心に読んでいったが、かなりグダグダな進行になってしまって、高橋さんにはだいぶ助けていただいた。ありがとうございました。
後半セッション「RSpec」
後半はもろはしさんにリクエストして前回出られなかったRSpecのセッションをもう一度。RSpecというのはBDD(Behaviour Driven Development)のためのフレームワーク。実行可能な仕様の記述とでもいうのだろうか。詳細はかくたにさん翻訳のチュートリアルを参照。
context "コードが4つともwhiteで、…" do setup do ... end specify "blackの判定は4であること" do @score.black.should_equal 4 end ... end
- オブジェクトが複数のstateをもつ場合(複数インスタンス)Unit Testより自然に書ける
- Rubyの場合、テストの名前に日本語が使える(Javaなら普通にできるけど)
- 従来の単体テスト(Unit Test)に相当する。Integration Testに適用するものも開発中
- データがいっぱいある場合、Test::Base(secondlifeさん作)のほうが楽
- should_equalはオブジェクトの一致比較なので、文字列比較には should == または should_eql を使う
日本Rubyの忘年会2006
総勢50人弱という大人数で盛大に忘年会が行われた。幹事のもろはしさん、Yuguiさん、ありがとうございました。
投稿者 4bit : 23:59 | コメント (0) | トラックバック (0)
2006年12月15日 (金)
URIにidを使いたくない [テクニカル]
Railsでmap.resourcesを使っているとURIが /users/1 とか /articles/99 って感じになる。
でも、URIに数字のid使うのダセeeeeeeeeeeee
/users/tkawa みたいにユニークな文字列が使いたい!!!!
ってことがあるはず。(そもそも内部的な値であるはずのidをURIに使っていいのかという議論もある)
そこでルーティングのidの部分を差し替えられるコードを簡単に書いてみた。
使い方
idの代わりにnameというカラムを使う場合、このように書く。
map.resources :users, :id => "name"
これで例えば GET /users/tkawa というリクエストがあった場合、{ "controller" => "users", "action" => "show", "name" => "tkawa" } というパラメータが渡される。よってfindするときはUser.find_by_name(params[:name])
とすればOK。
ただし、ヘルパーメソッドはなぜかuser_path(@user)
って書くとうまく動かないので、user_path(@user.name)
としなければならない。
でもこれで「URIにはユーザ名を使いたい!」とか「Wiki作るからURIにはページ名使えなきゃ困る!」ってときもバッチリ。
コード
プラグインでもenvironments.rbに直接書き込みでもお好きなようにどうぞ。ってか公開svnレポジトリ作らないとなー。
ActionController::Resources::Resource.module_eval do def id @options[:id] || "id" end def member_path @member_path ||= "#{path}/:#{id}" end def nesting_path_prefix @nesting_path_prefix ||= "#{path}/:#{singular}_#{id}" end end
投稿者 4bit : 19:23 | コメント (0) | トラックバック (1)
Singleton Resourcesが実装された [テクニカル]
「Railsのmap.resourcesに単数形リソース必要じゃん~」って2ヶ月前から言っていてプラグインとか作ったりしていたのだが、それが通じたのか(偶然だけど)Rick OlsonがSingleton Resourcesを実装してくれた!(Changeset 5701)
A singleton resource is global to the current user visiting the application, such as a user's /account profile.
そうそう、そういうことなんだよ~。やっぱり考えることは同じだね。
使い方
routes.rbに、map.resourcesと違って単数形で書く。
map.resource :account # map.resourcesと紛らわしいので注意
すると、こんな感じのルーティングが生成される。
HTTPメソッドとURL | AccountControllerのアクション | ヘルパーメソッド |
---|---|---|
GET /account | show | account_url account_path |
GET /account;edit | edit | edit_account_url edit_account_path |
PUT /account | update | account_url account_path |
DELETE /account | destroy | account_url account_path |
ちなみに、ヘルパーメソッドの*_urlと*_pathの違いは、前にRails勉強会で「よくわかんないけど*_pathに変わってきてるみたい」とか言っていたのだが、調べてみると*_urlがスキーム・ホスト名などを含んだURL(http://example.com/account)で*_pathがルートからのパスのみ(/account)になる。
これでセッションに存在する単数リソースや、現在ログイン中のユーザに紐づくリソースも表現できるようになった。めでたしめでたし。
投稿者 4bit : 12:34 | コメント (0) | トラックバック (0)
2006年11月25日 (土)
第九回XML開発者の日 [テクニカル]
「第九回XML開発者の日」で発表してきた。なんか予想外に緊張しまくりで、声も震えるしもう今考えると相当恥ずかしい。拙い発表ですみません。会場にRuby関係の方はあまりおられなかったようで、そういう意味では新鮮に聞いていただけた方も多かったのではと勝手に思っていますがどうだったでしょうか。
発表資料はこちら。
- 「Ruby on RailsにみるRESTfulアプリケーションの方向性」(PDF 約2MB)
スライド中の単数リソース対応プラグイン“map_singular_resource”についてはこちらの記事を参照。
以下各セッションのメモ・感想など。
REST Best Practices(山本陽平さん)
- Cool URI by TBL
- WebアプリとWeb API(Webサービス)を分けないのが重要
- resource orientedな設計
実際のWebサービスを取り上げての指摘はわかりやすかった。「WebアプリとWebサービスを分けない」というのはなるほど!と思った。Railsの方向性にも通じるところがある。
SOAのカウンターパートとしてのROA(Resource Oriented Architecture)はおもしろいかもしれない。
Ruby on RailsにみるRESTfulアプリケーションの方向性
発表資料参照
xfy カレンダーアプリへのGData実装(Masa and Takさん)
- GData: Google Data APIs
- OpenSearch1.1対応(q=文字列 など)
- hCalendar in RSS/Atom Feed
ジャストシステムが作っているxfyというXMLアプリを動かすためのプラットフォーム製品。そのカレンダーアプリにGDataを実装。
Plaggar: the duct tape of the Web(宮川達彦さん)
- Just like UNIX Pipe
- the UNIX Shell for Web2.0
- RSS(Feed) is the Standard I/O of the Web
「RSS(Feed)はWebのスタンダードI/O」という考え方にはなるほどと思った。それにマッチしているPlaggarが支持されるのもうなずける。
SkypeとMacromediaのBreezeというソフトを使ってサンフランシスコからのリモートプレゼンだった。映像も音声もクリアでかなり実用的と感じた。
ODFとOOXMLの標準化戦争(村田真さん)
- ODF(Open Document Format): OASIS, Sun, IBM
- OOXML(Office Open XML): ECMA, Microsoft
- 2006年5月にODFがISO標準になることが確実に
- 日本の官公庁はあんまり深く考えずWordを使ったりしてる。経産省は少し考えている。でも影響力のあるのは総務省
- 内部利用…コストが大事
- 国民向け…差別がないことが大事
- OOXMLがISO提出されたとき日本はどうするか?
規格の技術的な話ではなかったが、ストーリーとしておもしろかった。現状では公文書などにMS Wordなどが使われているのはやはりおかしい。会場の意見としても出ていたが、MSがOOXMLをオープン化することで、MS以外にOOXMLを生成できるツールが現れるかどうかがポイントではないかと思った。
microformatsに触れてみよう(上ノ郷谷太一さん)
- XFN: 人脈情報
- hCard: コンタクト情報
- hCalendar: スケジュール
- human readable first. machine second.
写真などをふんだんに使用したわかりやすくておもしろいプレゼンだった。手軽に使えるのであればMicroformatsを使ってみようかなと思わせる内容だった。
Syndy Chronicleの実装に見るMicroformats(山口琢さん)
- 年表をマシン処理可能にする(hCalendar)
- オーサリングツールが進化すれば…
- Syndy: OPMLベースのアプリラウンチャー
- Syndy Chronicle: 年表ビューア・エディタ
ブログエディタへのMicroformatsの実装(後藤康成さん、室田直匡さん)
- feedpathのブログエディタ。ブログサービスにMicroformatsでpostできる
- WSSE認証のパターンで困る
- hReview(本・雑誌・音楽…)
- hCalendar
- テンプレートも自由に編集可
非常に機能が充実したブログエディタだった。ブログツールもそれぞれ記事作成・編集用のWebサービスを持っていることから、このような外部エディタが出てくるのは必然ともいえるが、それ以外にも地図APIやページのスクリーンショットを撮るAPIなども利用していることから、大いにWebのメリットを活かしているアプリケーションだと感じた。このようにMicroformatsが利用しやすくなっていけば、もっと普及するのではないかと感じた。
投稿者 4bit : 08:53 | コメント (1) | トラックバック (0)
2006年11月20日 (月)
ActiveResourceはRails1.2ではリリースされません [テクニカル]
via id:moroさんの日記&id:secondlifeさんの日記。
Changeset 5554 - Ruby on Rails - TracActive Resource will not ship with Rails 1.2
うはw
発表にActiveResource含めなくてよかった~。まあ、どっちにしても僕は1.2の目玉はActiveResourceじゃなくてmap.resourcesだと勝手に思っていたので、問題ないんだけどね。
投稿者 4bit : 16:38 | コメント (0) | トラックバック (0)
2006年11月19日 (日)
Rails勉強会@東京 第12回 [テクニカル]
ポジションペーパーは、ネタがなかったというのもあるが時間がなく、初めて持っていけなかった。反省。
前回ノートパソコンをまたまた購入しなかったと言っていたのだが、今度こそ購入した(10/22)。中古だけど。Let's note R3。小さくて軽くてバッテリー持っていいぞ~。このマシンで前半セッションのプレゼン。
前半セッション「Ruby on RailsにみるRESTfulアプリケーションの方向性」
前半セッションは、今度の金曜日に開催される「第九回XML開発者の日」の発表の練習のセッションということで、オーナーを務めさせてもらった。人数は僕を含めて3人とかなり少なかったのだが、まあ練習ということもあるし本番では時間の都合でカット予定のところも含めて発表した。
緊張感がなかったせいか(?)かなりグダグダなしゃべりになってしまったのだが、そのへんは本番での修正事項としておいて、いろいろ意見ももらえたのでよかった。RSpecのセッションのほうにも出たかったけど、もろはしさんにぜひもう1回開催してもらおう。
後半セッション「いまこそARと外部キーについて考える」
後半は、舞波さんの外部キーについての提言&コーディングセッション。郵便番号や都道府県などのデータを持たせる場合、
- テーブルを作ってオートナンバーのidをサロゲートキー(代替主キー)とする
- テーブルを作って郵便番号や都道府県JISコードを主キーとする
- テーブルを作らずハッシュなどですませる
のどれにするか? という問題で、従来のRailsの考え方では1だったが、2のほうが便利なこともあるのでは?という提言。たしかに○○コードを使う場面では2もアリなのかなぁと思った。
そして、それを実現するためbelongs_toに:association_foreign_keyオプションを持たせるコーディング。スピード感のあるコーディングだった。
そして、あとはパッチを投げるだけなのだが、問題はコミッタにこのパッチが必要だということを英語で説得するのが難しいということ(笑)。has_one :throughにも同じようなことが言えるが、方向性の違いがあるとなかなかつらいね、こういうのは。
懇親会その後
関西のRails勉強会のようすを聞いたり、PythonのDjangoというフレームワークについて話したり。t-wadaさんがいらっしゃっていたので、最後にちょっとだけ挨拶できたのでよかった。
XML開発者の日で発表します
ということで、24日(金)の「第九回XML開発者の日」で、「Ruby on RailsにみるRESTfulアプリケーションの方向性」というタイトルで発表します。内容は、日本Rubyカンファレンス2006でのDHHの発表のREST関係部分+αという感じです。Rubyにあまり関わりがない人にも、あのDHHの発表の重要な方向性を少しでも伝えられればと思っています。あまり慣れていないので拙い発表になるとは思いますが、よろしくお願いします。
投稿者 4bit : 23:22 | コメント (0) | トラックバック (0)
2006年10月16日 (月)
Rails勉強会@東京 第11回 [テクニカル]
前回に引き続き、毎月恒例のRails勉強会に参加。今回で第0回から数えるとちょうど1年経つことになるのかな。Railsの世界は流れが速い。
ポジションペーパーは、「Nested Resourcesとmap_singular_resourceプラグイン」ということで前のエントリそのまま。
前回ノートパソコンを購入するとか言っておきながらいまいち踏ん切りがつかずまだ購入できていない…。ということでまたノートなしでの参加。
前半セッションは「map.resourcesを知っておきたくはないか?+Beast」。map.resourcesといえば一応僕の得意分野ということでオーナーのもろはしさんを差し置いてのりおさんと2人でダラダラと進めちゃった感じ。参加された方はあんな感じでわかってもらえたでしょうか?わからなかったらすみません。
map.resourcesやRESTfulについてよくまとまっていた「PeepCode RESTful Rails Cheat Sheet」をポジションペーパーの裏側に印刷しておいたので、それに挙がっていることを一通り説明していった。
あとそれに沿ってRESTfulに作られた掲示板システムであるBeastについてもソースを読む予定だったが、そこまでたどり着かず。個人的にチラッと見た印象ではコード量も少ないしシンプルでわかりやすいなと思った。
後半セッションは「激論ABD」。最初はダラダラセッションかと思われていたがまったくそんなことはなく、まさしく激論が交わされた。たぶんあのメンバーの中であんまりついていけてなかったのは自分だけ。なんか頭が働いてなかった…。心より恥じる。ていうかこんなことではABD飲み会についていけないぞ。ってことで復習するので他の方(もろはしさんとかYuguiさんとか)のレポートを期待。
懇親会ではYuguiさんの記事の原稿を見せてもらった。おもしろい。Principle of least surpriseやConvention over Configurationなどのことについて、突っ込んだらここまで書けるのかと感心した。またもろはしさんには今自分の中でちょっとひっかかっているワークフロー・ワークステートとRailsの関係について聞いた。やっぱり理想的にはモデル(DB)から導出できるべきって感じか。今はまだ深追いできないかなという感じがした。
投稿者 4bit : 15:11 | コメント (0) | トラックバック (0)
2006年10月05日 (木)
map_singular_resourceプラグイン [テクニカル]
きのうのエントリに書いたが、Railsのmap.resourcesに対応するリソースは複数形で、複数あることが前提とされていた。それじゃ単数(1つしかない)リソースの場合どうするんだよ、ってことで、単数リソースに対応するmap_singular_resourceプラグインを作ってみた。
使い方
routes.rbに、map.resourcesと違って単数形で書く。
map.resource :person # 紛らわしいので map.singular_resource :person も可
すると、だいたいこんな感じのルーティングが生成される。
HTTPメソッドとURL | PersonControllerのアクション | Route Name |
---|---|---|
GET /person | show | person |
POST /person | create | person |
GET /person;edit | edit | edit_person |
PUT /person | update | person |
DELETE /person | destroy | person |
map.resourcesからnewとindexを除いたような感じ。省略したけど、拡張子の:formatにももちろん対応している。:controller指定やアクション追加などのオプションももちろん使える。
has_one関係に使う
実はあんまり単体で使うことは少ないかもしれない。使われるとすればNested Resourcesで、モデル間の関係がhas_oneになっているときだ。
例えばUser has_one :profileというモデルの関係があったとすると、routes.rbにこのように書く。
map.resources :users do |user| user.resource :profile end
すると、こんな感じのルーティングになる。
HTTPメソッドとURL | ProfileControllerのアクション | Route Name |
---|---|---|
GET /users/:user_id/profile | show | profile |
POST /users/:user_id/profile | create | profile |
GET /users/:user_id/profile;edit | edit | edit_profile |
PUT /users/:user_id/profile | update | profile |
DELETE /users/:user_id/profile | destroy | profile |
きれいなURLでhas_oneの関係に対応しているのがわかる。
ソースは思いっきり手を抜いてactionpack/lib/action_controller/resources.rbからコピー&ペーストで作ったのでDRYじゃないし、相変わらずテストもないので不具合あるかも。でも使う機会はきっとあるような気がするなー。
投稿者 4bit : 17:43 | コメント (0) | トラックバック (0)
2006年10月04日 (水)
Nested Resources (map.resourcesのススメ その3) [テクニカル]
「map.resourcesのススメ」シリーズ第3回。
前回map.resourcesのオプションの利用方法を解説した中に、このようなものがあった。
特定のパラメータを必須にしたい場合
例えば、特定のグループ(ID)に属するユーザの一覧を表示したいとき、
map.resources :usersとするとURLは
/users?group_id=1
のようにgroup_idをパラメータに取ることになるが、あるグループに属するというのを標準にしたければ、
map.resources :users, :path_prefix => "/groups/:group_id"とすると、URLは
/groups/1/users
とすっきりと表記できる。
これは、モデルがhas_manyの関係のときに非常に一般的なので、Nested Resourcesとして、このようにシンプルに書けるようになった。
map.resources :groups do |group| group.resources :users end
これは、
map.resources :groups map.resources :users, :path_prefix => "/groups/:group_id"
と書いたのと同じ効果を持つ。この場合はモデルはGroup has_many :usersって感じになる。
そこでふと思ったのだが、has_manyに対応するものがあるのに、has_oneに対応するものはないのか? そもそもRailsでのリソースって複数形のみで、単数(1つしかない)リソースが想定されてない気がする。has_oneもしくはカラム単体に対応するリソースがあれば、もっとモデルを柔軟に表現できるんじゃないか?
単数限定リソースを実現するmap_singular_resourceプラグインでも作ってみるか…。
投稿者 4bit : 17:10 | コメント (0) | トラックバック (0)
2006年09月28日 (木)
scaffold_resource_generator [テクニカル]
Edge Railsに、scaffold_resource_generatorというリソース(map.resourcesで指定するもの)用のscaffold generatorが新しくできた(Changeset 5147)。これを使えばコントローラとモデル(とマイグレーション)を一気に作ってくれるというシロモノ。
使い方
ruby script\generate scaffold_resource <リソース名> [カラム名:型名] ...
第3引数にリソース名(※単数形であることに注意)、第4引数以降にテーブルのカラム名と型名を“:”で区切ったものを並べていく。型名はマイグレーションで指定するものと同じ。例えば、AWDwR本のDepotアプリケーションのproductを作るならこんな感じ。
ruby script\generate scaffold_resource product title:string description:text image_url:string price:float date_available:datetime
これでモデルとマイグレーション、さらに通常のscaffoldのRESTバージョンともいえるコントローラ(ProductsController)とビュー、テストの雛形が生成される。生成後にroutes.rbにmap.resources :products
を書くのを忘れないように。こっちは複数形。
前のmap.resourcesの紹介記事の中では通常のscaffoldを使うパターンを紹介したが、これからはscaffold_resourceで一発になりそうだ。個人的には通常のscaffoldがリソース対応に書き直されるのかなと思っていたが、別にリソース専用のscaffoldができたということは、Railsで作る全部がリソースになるのではなく、リソースの場合とそれ以外の場合とを区別して使ってねという意図の表れなのかもしれない。とはいえ、個人的には全部リソースというのがすっきりして好きなんだけどね。リソースにするとActiveResourceでアクセスできるというおまけもつくし。
投稿者 4bit : 09:57 | コメント (0) | トラックバック (0)
2006年09月25日 (月)
Rails勉強会@東京 第10回 [テクニカル]
ポジションペーパーは、「DepotにABDを適用してハマった」ということで前のエントリそのまま。
いつもノートPCなし(すげえ古いのしか持ってない)で参加しているのだが、今回は特にノートなしでは厳しかった。前半セッションは「Rails1.2を先取り」でChangesetをひたすら読んだ。後半セッションは「AWSに挑む」でActionWebServiceのソースを読んだ。
Rails1.2はなんかかなり早く出る予定らしくて、map.resourcesばかり見てたけどActiveResourceとかresource_feederだとかscaffold_resourceだとかいろいろあるらしくて、今後のRailsの方向性に注目してる身としてはちゃんと追っかけないとやばい。
AWSは正直ぜんぜんついていけてなかった…。個人的にRESTafarianではあるけど今後仕事でSOAPとかもやりそうなのでこのへんは理解しておかないといけないのだが…。まあ実際やるときはJavaでやることになると思うけど。
懇親会ではJavaのグチの話になったりして、「XMLを設定ファイルに使うのはやめよう!」という僕と同じことを考えている方がいたりした(笑)。
次回までにはノートPCを購入している予定なのでやっと持っていけるかなー。
投稿者 4bit : 11:22 | コメント (0) | トラックバック (0)
2006年09月21日 (木)
DepotにABDを適用してハマった [テクニカル]
「RailsによるアジャイルWebアプリケーション開発(通称AWDwR)」にサンプルとして載っているDepotアプリケーションにABDを適用して作り直したらどうなるだろう?というのを少し前にやっていたのだが、意外なところでハマったのでメモ。
テーブル設計はもともとのがこんな感じ。典型的なマスタ・ディテール形式。
ABDを適用したら、このようになる(はず)。line_itemsからFKが消えて、FKを集約したorder_activitiesというテーブルができる。
LineItemクラスではこんな感じで has_one :through 使ってる。Yuguiたんプレゼンツ。
class LineItem < ActiveRecord::Base has_one :order_activity has_one :product, :through => :order_activity ... end
問題は、CartというDBには載らないクラスの存在。このオブジェクトはセッションに入っていて、内部にLineItemの配列を持つ。その使い方は、カートに商品が入るたびに、すでにある商品ならすでに入ってるLineItemのquantityを+1し、新しい商品ならLineItemをnewして入れるという感じ。そして、注文を確定するときにOrderを作ってそこにCartに入っていたLineItemをまとめてくっつけて一気にsaveするのだ。つまりCartに入っている間はLineItemはsaveされていないというのがポイント。
さて、もともとのテーブル設計ならLineItemにはproduct_idが入っているので問題ないのだが、ABD版では入っていない(FKがないんだから当然だが)。そこで has_one :through でproductをたどれるようにしたいのだが、LineItemはsaveされていないからidが無い! よってOrderActivityが作れないのだ。ガーン。
かといってLineItemをsaveしてしまうのも、CartがDBに入ってない単なる一時データなのにその中にあるLineItemがいちいちDBに入るってのはどうなんだ、と。さてこういうときどうするのが一番いいのでしょう?
まあ端的に言えばDB上にないデータの関連はたどれませんよってだけなのだが、ActiveRecordはnewして一時的にsaveせずに使うという使い方もけっこう想定されてるような気がする。それともレアケース?
あと直接関係ないけどDepotアプリのREST適用版ってのも試作していて、そっちはなかなかいい感じ。AWDwRの将来の版ではこういうのが載るようになるのかな。
投稿者 4bit : 17:53 | コメント (0) | トラックバック (0)
2006年08月27日 (日)
Rails勉強会@東京 第9回 [テクニカル]
ポジションペーパーは、「祝・SimplyRestfulプラグインマージ記念 map.resourcesのススメ」。以前の2つの記事を「URLのaction撲滅しようよ(Cool URI)」という観点から簡単にまとめたもの。
前半セッションは「LL Ringの家計簿アプリの中身を詳しく」。きのう開催されたLL Ringの「キミならどう書く」セッションで作成された家計簿アプリをかずひこさんの紹介で見てみる。
- テーブル設計
- 勘定科目
- id、名前、種類
- 仕訳
- id、日付、金額、借方id、貸方id
- あらゆるタグ
- 日付もタグ(!)
- 検索インターフェイスが統一される
- acts_as_taggable(gem版)利用
- rake stats便利
- annotate_modelsプラグイン
- rake annotate_models でモデルクラスの先頭に対応テーブルのスキーマコメントを埋め込んでくれる
- Lightbox Jojoで「ドドドド」「ドッギャァ~ン」(笑)
- グラフを描くにはgruffプラグインを利用
あらゆるタグ利用で、検索インターフェイスが統一されるというアイデアはすごい。
最後のほうでかずひこさんが話を振ってくださってmap.resourcesについて少ししゃべる時間をもらった。RESTセッションできたらいいなぁとか思いつつぜんぜんできてないけど…。ノートPCがないというのもあるし。もう買うしか。
後半セッションは、「has_one :throughを作る」。Yuguiさんが作っているhas_one :throughを正式にRailsのパッチとして提出するために、既存のActiveRecordの単体テストのどのモデルを利用するべきかなどいろいろ話し合った。
舞波さんも加わってABDの話題(はぶさんがしばらく封印とか言ってるし。。)も出て、どういうモデリングが必要なのか議論。とりあえずhas_one :throughのテストの方針は決まり、あとはパッチを受け入れてもらうためのユースケースを考えねばということに。それとは別に、このままいくと消えかねないhabtmの復活案も出て、内容の濃い時間だった。ちょっと後から思い出して整理しとこう…。
投稿者 4bit : 22:21 | コメント (0) | トラックバック (0)
2006年08月04日 (金)
舞波本きたー [テクニカル]
「優しいRailsの育て方」、通称「舞波本」購入。
あと最初に断っておきますが、そういうのは無いです。至って真面目な本です、ごめんなさい。
いや、最初のほうAA満載なんですけどwww AAで解説って斬新すぎw しかも舞波とか桃子とか至るところに出てくるしw
内容はRails一通りおさえたリファレンスって感じで役に立ちそう。「パッチを作る」なんてセクションもあるし。最初少しだけ書いたhtpasswdプラグインも載ってる。
あと、一緒にyoheiさんおすすめ(というか執筆なさった)WEB2.0キーワードブックも購入。著者陣が豪華。
「RESTって何?」って聞かれたときに、これ読んでってyoheiさんの章を安心して見せられます。
投稿者 4bit : 20:51 | コメント (0) | トラックバック (0)
2006年08月02日 (水)
map.resourcesのススメ(「SimplyRestfulプラグインのススメ」改め) [テクニカル]
前の記事に引き続き、map.resourcesのオプションの利用方法を解説。DHHによってRails本体にマージされちゃったので、「SimplyRestfulプラグインのススメ」って言えなくなっちゃった…。てことで改題。
コントローラ名を変える
コントローラにモデル名の複数形をつけるのが気に入らない場合、コントローラ名を好きな名前に変えることもできる。map.resourcesにオプションでこのように指定してやるだけだ。
map.resources :people, :controller => "person"
特定のパラメータを必須にしたい場合
例えば、特定のグループ(ID)に属するユーザの一覧を表示したいとき、
map.resources :users
とするとURLは
/users?group_id=1
のようにgroup_idをパラメータに取ることになるが、あるグループに属するというのを標準にしたければ、
map.resources :users, :path_prefix => "/groups/:group_id"
とすると、URLは
/groups/1/users
とすっきりと表記できる。
このとき同時にオプション :name_prefix => "groups_" と指定すれば、名前付きルートの名前の最初に groups_ がつき、通常と区別することができる。よって、
map.resources :users # 通常のユーザ map.resources :users, :path_prefix => "/groups/:group_id", :name_prefix => "groups_" # グループに属するユーザ
のように2種類同時に使うことも可能。
CRUDから外れたアクションを追加する場合
DHH曰く、「ここは敢えて CRUD から外れよう、と判断するのはありだと思う。こういうのはバランスの問題で、極端に走るのがいいとは限らない」。
例えば、ユーザにマークをつけるmarkアクションを追加する場合、このように指定する。
map.resources :users, :member => { :mark => :post }
これで mark_user という名前付きルートで
/users/1;mark
という形のURLがHTTP POSTに限って使えるようになる。
オプションとして与えるハッシュのキーはそれぞれ以下のように対応している。
キー | URL |
---|---|
:member | /users/1;アクション名 |
:collection | /users;アクション名 |
:new | /users/new;アクション名 |
ハッシュの値のハッシュ(ややこしい)は、{ アクション名 => 許可されるHTTPメソッド } の形で指定する。
あんまりすっきりした形で指定できないなあと思うけど、そもそもCRUDから外れたアクションはあまり追加しないほうがいいわけで、乱用は禁物だ。
最後に
Rails本体にマージされたということで、いよいよRailsアプリがCRUDに、RESTfulになる日が近づいてきている感じがする。正直個人的には1.2の機能ではActiveResourceよりこっちのほうがうれしい。Edge Railsを厭わない人は今からでもどんどん使ってみて、CRUDな設計・実装をぜひ実現してください。僕もいろいろ使ってみてABDとの相性などを調べていきたいと思う。
参考
- Refactoring to REST
IconBuffet.comというサイトをSimplyRestfulプラグインを使ってRESTにリファクタリングしてみましたというメモ。 - Getting RESTful on Rails (Part 1)
英語でのSimplyRestfulプラグインの解説。こっちのほうがわかりやすいかも? - SimplyRestful plugin backport
SimplyRestfulプラグインのRails1.1.4向けバックポート。ルーティングのセパレータにセミコロンやピリオドが使えないのでなかなかつらいようだ。使ったことないのでどなたか使ってみてください。 - Request Routing Plugin
上のバックポートを動かすのに必要なプラグイン。ルーティングの:conditionsに:subdomain, :domain, :method, :port, :remote_ip, :content_type, :accepts, :request_uri, :protocolが指定できるようになるという、なかなか使えそうなプラグイン。
投稿者 4bit : 19:04 | コメント (0) | トラックバック (0)
2006年07月31日 (月)
SimplyRestfulプラグインのススメ [テクニカル]
はじめに
日本Rubyカンファレンスから早2ヶ月弱、DHHのセッションに衝撃を受けた人(僕)もそうでない人もいたと思うが、このセッションで繰り返し語られたCRUDな設計・実装がこれからのRails界の潮流になっていくのは間違いないと思う。
ちなみにまだ見てない人はこちら。
- 「Discovering a world of Resources on Rails」スライド (PDF)
- 日本語による解説 by あんどうさん
そこで、このDHHのセッションに出てくるCRUDな実装をサポートするプラグインが、SimplyRestfulプラグインだ。
(って書き始めたのはいいのだが、Edge Rails専用だと後で知ってちょっと一般には薦められないかも…と思い始める。まあでも書いちゃったので載せる)
8/2追記:ついにRails本体にSimplyRestfulプラグインがマージされてしまった(Changeset 4637)。やってくれるぜDHH!ということで少し使い方が変更されているので修正。
インストール
インストールは不要。そのかわり最新のEdge Railsを使おう。ruby script/plugin install http://dev.rubyonrails.org/browser/plugins/simply_restful
rake rails:freeze:edge
基本的な使い方
[Model名の複数形]Controllerがあることを前提とする(名前の変更も可能)。例えばPeopleControllerであれば、routes.rbにこのように書く。
ActionController::Routing::Routes.draw do |map|map.resource :personmap.resources :people # 複数形に変更 end
この1行だけで、以下のルート、名前付きルートが生成される。
Routes
Route | Significant Keys | Requirements | Conditions |
---|---|---|---|
/people | [:controller, :action] | {:controller=>"people", :action=>"create"} | {:method=>:post} |
/people.:format | [:format, :controller, :action] | {:controller=>"people", :action=>"create"} | {:method=>:post} |
/people | [:controller, :action] | {:controller=>"people", :action=>"index"} | {:method=>:get} |
/people.:format | [:format, :controller, :action] | {:controller=>"people", :action=>"index"} | {:method=>:get} |
/people/new | [:controller, :action] | {:controller=>"people", :action=>"new"} | {:method=>:get} |
/people/new.:format | [:format, :controller, :action] | {:controller=>"people", :action=>"new"} | {:method=>:get} |
/people/:id;edit | [:id, :controller, :action] | {:controller=>"people", :action=>"edit"} | {:method=>:get} |
/people/:id.:format;edit | [:id, :format, :controller, :action] | {:controller=>"people", :action=>"edit"} | {:method=>:get} |
/people/:id | [:id, :controller, :action] | {:controller=>"people", :action=>"destroy"} | {:method=>:delete} |
/people/:id | [:id, :controller, :action] | {:controller=>"people", :action=>"update"} | {:method=>:put} |
/people/:id | [:id, :controller, :action] | {:controller=>"people", :action=>"show"} | {:method=>:get} |
/people/:id.:format | [:id, :format, :controller, :action] | {:controller=>"people", :action=>"show"} | {:method=>:get} |
Named Routes
Name | Route | Significant Keys | Requirements | Conditions |
---|---|---|---|---|
edit_person | /people/:id;edit | [:id, :controller, :action] | {:controller=>"people", :action=>"edit"} | {:method=>:get} |
formatted_new_person | /people/new.:format | [:format, :controller, :action] | {:controller=>"people", :action=>"new"} | {:method=>:get} |
new_person | /people/new | [:controller, :action] | {:controller=>"people", :action=>"new"} | {:method=>:get} |
formatted_people | /people.:format | [:format, :controller, :action] | {:controller=>"people", :action=>"index"} | {:method=>:get} |
people | /people | [:controller, :action] | {:controller=>"people", :action=>"index"} | {:method=>:get} |
person | /people/:id | [:id, :controller, :action] | {:controller=>"people", :action=>"show"} | {:method=>:get} |
formatted_person | /people/:id.:format | [:id, :format, :controller, :action] | {:controller=>"people", :action=>"show"} | {:method=>:get} |
formatted_edit_person | /people/:id.:format;edit | [:id, :format, :controller, :action] | {:controller=>"people", :action=>"edit"} | {:method=>:get} |
Scaffoldから使う
PeopleControllerに必要なアクションメソッドは、index, show, new, create, edit, update, destroyの7つ。これはScaffoldで生成されるコントローラとほぼ対応しているのでScaffoldから使ってみるのがお手軽。Scaffoldから使う場合、変更するところは以下の通り。
- listアクションはindexアクションに変更
コントローラのlistメソッドの中身は全部indexメソッドに移す。コントローラとビューのaction => 'list'
もすべてaction => 'index'
もしくはpeople_url
(後述の名前付きルートURL)に書き換える。結構いっぱいあるので注意。そしてビューのlist.rhtmlをindex.rhtmlにリネーム。 - コントローラの
verify
を消す - edit.rhtmlの
start_form_tag
にオプション:method => :put
をつける
<%= start_form_tag :action => 'update', :id => @person %>
↓<%= start_form_tag({ :action => 'update', :id => @person }, :method => :put) %>
<%= start_form_tag person_url(@person), :method => :put %>
でも可。 - index.rhtmlの
link_to 'Destroy'
にオプション:method => :delete
をつける
<%= link_to 'Destroy', { :action => 'destroy', :id => person }, :confirm => 'Are you sure?', :post => true %>
↓<%= link_to 'Destroy', { :action => 'destroy', :id => person }, :confirm => 'Are you sure?', :post => true, :method => :delete %>
<%= link_to 'Destroy', person_url(person), :confirm => 'Are you sure?', :post => true, :method => :delete %>
でも可。
ほかにもあるかもしれません…。
名前付きルートURLを活用する
名前付きルートがたくさん定義されているので、付随してxxx_urlメソッドが使える。例えばperson編集画面へのリンクは、
link_to 'Edit', :action => 'edit', :id => person
の代わりに、
link_to 'Edit', edit_person_url(person)
と書ける。可読性もよくなるのでぜひ使うようにしよう。
ということで
このSimplyRestfulプラグインを使うことで、CRUDな設計・実装をせざるを得なくなるというのが重要なところだ。Constraints are liberating. 制約が自由をもたらす。
(次回、オプションの利用方法に続く)
投稿者 4bit : 18:56 | コメント (0) | トラックバック (0)
2006年07月24日 (月)
Rails勉強会@東京 第8回 [テクニカル]
昨日行われたRails勉強会に参加。
ポジションペーパーは、「ABDはやり過ぎてもいいのか否か」。結論から言うと「やり過ぎてもよい」なのだが、詳細は調べてあとで書く(つもり)。
今回ちょっとセッション選びを間違えちゃったかな…。「RailsでABD実践」のセッションが後半にも行われると聞いて前半選ばなかったのが失敗だった。結局前半は「Rails Chatのソースを読む」を選択。のりおさん作のRails Chatのソースを読んだ。Flashのxmlsocketを使って、サーバとブラウザ間にコネクションを張ってリアルタイムにページの更新を行ったりできる「Juggernaut」(の古いバージョン)を使っているのが特徴。詳細な情報はこのへんに。
後半は「BackgrounDRbを触ってみる」。よしみかんさんのまとめ「I sort my thought... - BackgrounDRbについて調べてみた」を見ながらみんなで動かしてみた。僕はノートPCを持っていなかったこともあり少しうとうとしてしまった。心より恥じる。
相変わらずREST好きな僕としては、もろはしさんの「ABDとCRUD/Railsは相性がいい」やYuguiさんの「ABDはアジャイルで変更に強いDB設計だ」あたりの意見にかなり強い興味があるので、次回もセッション希望というか、次回までに自分でもそのへん(とくにRailsやRESTにどう生かせるのか)を予習して臨みたい。というか予習したらblogに書く。あと個人的におすすめのSimplyRestfulプラグインの(ある程度詳細な)紹介をURLのaction撲滅運動(?)と絡めて書く。
追記:デブサミでのはぶさんのセッション資料にABDの記述を発見。デブサミのときからあったのか…。Seasar Conferenceの資料はあるみたいだけどサーバが死んでるのか見れません。見れました。
- Developer Summit 2006 - 楽々ERDレッスンLive (PDF)
- Seasar Conference 2006 Spring - EJB3時代のERDレッスン~Activity Based Datamodel (PowerPoint)
投稿者 4bit : 14:00 | コメント (0) | トラックバック (0)
2006年06月19日 (月)
Rails勉強会@東京 第7回 [テクニカル]
ポジションペーパーは、ここのブログに書いてた「DHH loves REST!?」をマイナーチェンジしたものです。ogijunさん曰く「カンファレンスに行ってないのに煽ってる人がいる」とズバリ。はい、煽ってるつもりです(笑)。
今回は初めて恵比寿のドリコム東京事務所での開催。入ったらいきなりエントランスホールにビリヤード台が置いてあってびっくり。なんか煮詰まったときには遊んだりするそうだが、もし僕がこんな環境にいたらずっと遊んでしまいそうで恐い。スヌーカー台だったらもっとよかったのに(大きすぎて無理)。他も非常にきれいですばらしい環境でした。
前半セッションは「DHHふたたび」。先日の日本RubyカンファレンスのDHHの講演の音声をおおもりさんが録音されていたので、それを聴こうというセッション。
非常に聞き取りやすい英語だった…と思うのだが、自分の英語力のショボさで「とりあえず聞いただけー」に終わる。情けない…。翻訳Wikiのあんどうさんのまとめを手元に置きながら聞いたのだが、これがなかったらぜんぜんわからなかった。感謝。
講演の内容でDHHがCRUDとは何回も言っていたがRESTとは言っていないことが少し気になったが、正確な意味でのRESTではないので避けた説、RESTの宗教論争に巻き込まれるのがイヤだった説とかいろいろ挙がっていた。あとで懇親会のときに高橋さんに聞いたら「深く考えてないだけでは」ということだったが(笑)。
あとカンファレンス参加者が多かったせいか、このセッションの参加者が5人と少し寂しかった気がした。
後半セッションは「ActiveRecordモデリング」。ソーシャルブックマークを作るならというお題で、DB設計とモデリング、実際にActiveRecordを使ったコード例を書いてみようという趣旨。
UserとPageがあって、多対多の関係なのでBookmarkを追加し、habtmはやめてhas_many throughを使おう…、みたいな感じ?(詳細はあまりメモってなかった)
あとの懇親会も含めて、YuguiさんがABD(Activity Based Datamodel; はぶさん提唱のDB設計法)を連呼していたのが印象的だった。不勉強だったのでちょっと調べてみるとなかなかエキセントリック(は言い過ぎか)で興味深い。
懇親会では、はぶにっきを読みつついろいろ話した。DHHの講演内容に関連してREST話も少し出た。高橋さんがREST原理主義(?)だというのが判明して、意外だった。
で、結論としてはとりあえず「楽々ERDレッスン」読もうね、ということに(笑)。買おう。
ヤドカリの人とほとんど一緒にいたのでかぶってしまってる(^^; とくに2次会が詳細です。
投稿者 4bit : 16:23 | コメント (0) | トラックバック (1)
2006年06月15日 (木)
DHH loves REST!? その2(ActiveResource) [テクニカル]
その1のつづき。ActiveResourceはさらにヤバイ。
ActiveResource
moroの日記 - 日本Rubyカンファレンス2006 二日目より
- ActiveResouce!!
- one more thingとか言うあたりがマカー
- Webサービスで得られる情報をActiveRecordライクに。
- 当然CRUDできる。
- Rails 1.2の目玉。
- 6/11 22:00 JST時点ではsvnツリーにもない模様。
- ValidationとかAssociationも使えるんですかね? Validationは大丈夫そう。
- RESTしか対応しない。SOAPとかXML-RPCは嫌いらしい。
- ちゃんと使う似は厳しい規約が必要(HTTPメソッドだけでCRUDできるとか)。Rails製アプリケーション間での使用が中心になるのかなぁ。
UIE Japan Developer Blog: 日本Rubyカンファレンス2006レポート (2006/06/11)よりDHH > 「XMLRPCもSOAPも使いたくない」
Rails1.2のtrunkにもまだ上がっていない様子のActiveRecordからCRUDを操作できるActiveResourceの話が面白い。URLをモデルの中に隠蔽して後は気にせず、AR同様に使うことができる。認証周りは、HTTP Authentication, Digest認証, SSLを誰かが作ってくれるかも。
Person = ActiveResorce::Struct.new do |p|
p.uri "http;//www.example.com/people"
p.credentials :name => "dhh", :password => "secreat"
end
david = Person.new(:name => "David")
# GET http://www.example.com/people
# <person><name>David<name></person>
# => Location: http://www.exapmle.com/people/2
david.find(:name => "David")
# PUT http://www.example.com/people
# <person><name>David Heinemeier Hasson</name></person>
# => (200 OK)
david.save
class ActiveResource::Base
uri "http://www.example.com"
credentials :name =>"dhh", :password => ""
end
何がヤバイって、「ActiveResourceでアクセス可能なAPI」という合意を得て、REST APIのデファクトスタンダードを形成する可能性があるってこと。細かいことを言えばfindの規約とかどうするかわからないけど、OpenSearchでもいいし、とにかくRailsの機能として実装すればそれがデファクトになりうる。
例えばすごく単純に、columnカラムがquerywordなリソースを取ってくるには/controller?column=queryword
とかでもいいんじゃない?pageとかとバッティングするのがちょっとまずいけど。
実はActiveResourceに似た構想はちょっと前に考えてたんだけど(とか言ってみるテスト)。僕の着想のベースは、Ajaxのクロスドメイン制約を回避するために、proxyのようなものが必要なので、それをRailsベースで作れないかなぁということだった。ActiveResourceができればproxyどころかもっと便利に使える。さすがDHH。ぜひ実現してほしい。
投稿者 4bit : 12:49 | コメント (0) | トラックバック (0)
DHH loves REST!? その1(RESTプラグインの紹介) [テクニカル]
日本Rubyカンファレンス2006。DHHのセッション内容がヤバイ。チケットが入手できなかったので聞けなかったけど(発表資料くれー)。タイトルが「One controller, many ins, many outs」→「Discovering a world of Resources on Rails」。うひょー。世界リソース発見。
Blogなどでレポートを書いてるみなさんありがとうございます。以下関係部分を引用。
moroの日記 - 日本Rubyカンファレンス2006 二日目より
- CRUD素晴らしい
- HTTPのメソッドで、既にCRUDできるじゃん。HTTPのPOST GET PUT DELETEメソッドとコントローラのアクションcreate show update destroy、SQLのINSERT SELECT UPDATE DELETEは直接に対応してるじゃん。
- これを繰り返すのはDRYじゃないよね?
- ActionController::Routing::Routes.draw{|map| map.resource "/controller/:id" }という宣言をすると、HTTPの各メソッドによって"/controller"を叩くことでCRUDのお好きな操作をば。
UIE Japan Developer Blog: 日本Rubyカンファレンス2006レポート (2006/06/11)よりREST
HTTPメソッドだけでCRUDできるよね。複数個所に分かれているのはDRYじゃないし、POST /peopleform_forでmethodを定義 ブラウザのformでputやdeleteできないのでmethodはhiddenで渡す (javascriptで~~と言いかけて訂正していたのでjsのputやdeleteも使うようになるのかも) また、CRUDはゴールでなくゴールを目指す肝であり設計の技法ですよ。
GET /people/1
PUT /people/1
DELETE /people/1
このへんから推察するに、Simply RESTfulプラグイン、RESTful Railsプラグインの機能(に近いもの)を標準に取り込むということでは?
Simply RESTfulプラグイン
routes.rbにmap.resourceというメソッドを追加。map.resource :item
と書くと以下のようなルーティングが生成される。
VERB | URL structure | Rails method |
---|---|---|
Display | ||
GET | /items | index |
GET | /items/1 | show |
Create | ||
GET | /items/new | new |
POST | /items | create |
Remove | ||
DELETE | /items/1 | destroy |
POST | /items/1?_method=DELETE | destroy (synonym) |
Update | ||
GET | /items/1;edit | edit |
PUT | /items/1 | update |
POST | /items/1?_method=PUT | update (synonym) |
POST | /items/1;complete | complete |
これはDHHの言ってるルールと符合する。
RESTful Railsプラグイン
routes.rbにmap.connect_resourceというメソッドを追加。map.connect_resource :item と書くと以下のようなルーティングが生成される。
URL structure | Rails method |
---|---|
/item | new |
/items | collection |
/items/1 | by_id |
さらにコントローラに
resource :collection do |r|
conditions << @books = Book.find(:all)
r.post do
...
end
end
resource :by_id do |r|
r.put do
...
end
r.delete do
...
end
end
などと書いて処理を行う。
その他条件リクエスト処理機能(リクエストされたリソースが更新されていなければ304 Not Modifiedを返すなど)など。詳しい使い方はXML.com: Putting REST on Rails参照。
その2へつづく。
投稿者 4bit : 11:49 | コメント (0) | トラックバック (0)
2006年05月25日 (木)
簡単認証プラグイン試作 [テクニカル]
「ヽ( ・∀・)ノくまくまー(2006-05-21)」より本格的な認証機能は不要なので、のーにゅーまでもう時間がない!みたいな必死なときに30秒くらいでとりあえずな認証をかけられるものがあると便利だとゆいたいです。とりあえず .htaccess でBasic認証をちょこっと置いてます、みたいな。
class AdminController < ApplicationController htpasswd :user=>"maiha", :pass=>"berryz" # 認証(何行でもかけて上から随時実行) htpasswd :file=>"/var/www/passwd/.htpasswd" # 外部ファイル指定も可能(htpasswd 互換) end
Basic認証でよければ、ってことで認証まわりの練習がてらプラグインをでっちあげてみた。
だいたい上の使用例の感じで動くはず。動かなかったらごめんなさい。
追記:くまくまーの中の人に添削してテストも書いてもらった!感謝! てことで使うときはこちらをどうぞ→「ヽ( ・∀・)ノくまくまー(2006-05-25)」
コード。汚いので添削希望。
module Htpasswd
def self.included(base)
base.extend(ClassMethods)
end
class Error < StandardError
define_method(:message) { 'Authorization is required' }
end
module ClassMethods
def htpasswd(options={})
if options[:file]
type = options[:type] || :crypted
IO.foreach(options[:file]) do |line|
user, pass = line.chomp.split(':', 2)
write_inheritable_array(:htpasswd, [{:user => user, :pass => pass, :type => type }])
end
else
write_inheritable_array(:htpasswd, [{:user => options[:user], :pass => options[:pass], :type => :plain }])
end
skip_before_filter :htpasswd_authorize rescue nil
before_filter :htpasswd_authorize
end
end
protected
def htpasswd_authorize
raise Htpasswd::Error unless auth_header = request.env['HTTP_AUTHORIZATION']
auth_type, auth_data = auth_header.split(' ', 2)
user, pass = Base64.decode64(auth_data).split(':', 2)
raise Htpasswd::Error unless pass.length > 0
auth_users = self.class.read_inheritable_attribute(:htpasswd)
auth_success = false
auth_users.each do |h|
case h[:type]
when :plain
if h[:user] == user and h[:pass] == pass
auth_success = true
break
end
when :crypted
if h[:user] == user and h[:pass] == pass.crypt(h[:pass])
auth_success = true
break
end
end
end
raise Htpasswd::Error unless auth_success
return true
rescue Htpasswd::Error => error
realm = "Authorization"
response.headers['WWW-Authenticate'] = response.headers['WWW-Authenticate'].to_a
response.headers['WWW-Authenticate'].push("Basic realm=\"#{realm}\"")
#headers['Status'] = '401 Unauthorized'
render :nothing => true, :status => 401
return false
end
end
投稿者 4bit : 16:34 | コメント (645) | トラックバック (0)
2006年05月21日 (日)
Rails勉強会@東京 第6回 [テクニカル]
ポジションペーパーは、「RESTプラグイン試作」というもので一応試作したのを置いときます。ただしモデルのテーブルのカラム名により動かない可能性あり。ソースも汚いです。
前半セッションは高橋さんの「Typoの改造」。Railsで作られているBlogシステムのTypoを改造してますという話。実は僕もTypoを改造しようかと思って(思っただけでやってない)、svn trunkのソースを眺めてみたことがあったので興味深い話だった。あとでruby-listを見てみたら関連スレッド([ruby-list:42174] i18n Typo and Ruby-GetText)を発見。
- 改造対象バージョンは2.6.0(stable)
- svn trunk(ver4系列)はテーブル構造とかすごくいろいろ変わってて追っかけるのが大変
- I18N化改造
- メッセージを日本語化
- Ruby-Gettext-Package(GNU gettextライクなツール)を使用
- RailsにインストールするとRakefileができる
- updatepo…viewの _(...) からpotファイルを生成
- makepo…poファイルからmoファイルを生成
- マルチユーザ化改造
- URIにユーザIDを含めて、ArticlesやCommentsテーブルのuser_idから該当のユーザのデータだけを抽出して表示する
僕は存じ上げなかったのだが、JIS文字コード関係などで有名な芝野先生が来ていらっしゃって、国際化(多言語化)の話でなかなか激論が交わされた。
後半はsecondlifeさんの「Mongrelのソースを読む」。Ruby(一部C)のWebサーバであるMongrelの紹介とソース読み。しかし僕はノートパソコンを持っていなかったこともあってちょっと取り残された感じ。ほとんどメモも取っていなかった。
- Mongrelは高速でRailsにも対応してるのですぐ使える
- WEBrickは起動が遅い→開発意欲低下
- Mongrelならさくさく起動するので快適
- でも信頼性はまだ疑問?
- lighttpdよりは遅いのでdevelopment環境用に使うとGOOD
都合で懇親会は遠慮させてもらったが(井上さんすみませんでした)、有意義な一日だった。僕もRESTプラグインがもう少し実用的になればそれをネタに1セッションやりたいなぁとも思ったのだがなかなか難しそうだ…。
投稿者 4bit : 23:59 | コメント (0) | トラックバック (1)
2006年04月17日 (月)
Controller#respond_toはWeb ApplicationとWeb Serviceの垣根をなくす!? [テクニカル]
昨日のRails勉強会のポジションペーパーの内容を起こしてみた。内容はほとんどヽ( ・∀・)ノくまくまーからのインスパイヤなんですが。なので詳しくは元記事を見てください。このblogの記事の流れとしてはずいぶん前の「RESTful Web Applicationの可能性」からの続きの内容という感じです。
Rails 1.1からの新機能として、Controller#respond_toというメソッドができた。どういうものかというと、
- HTTP/1.1リクエストヘッダ中のAcceptフィールドを見る
- Content-Typeに応じてリクエストのデータ形式を自動変換してくれる
- 従って、コントローラは同じロジック(action)で対応できる
- Acceptの種類(mimeタイプ)に応じて、実行する描画処理を指定できる
- "*/*"が指定された場合はrespond_toの最初の定義を実行する
respond_to do |type|
type.html { render }
type.yaml { render :text => @resource.to_yaml }
type.xml { render :text => @resource.to_xml }
end
さらにREST的インターフェイスを実現するために自前でコントローラを実装する。
class RestController < AbstractRestController
def resource
model_name = params[:model].camelize
model = eval(model_name) # *超危険*
if params[:id] # idがあればMember URI
id = params[:id]
@resource = model.find(id)
case request.method
when :get
# Retrieving a Resource
respond_to do |type|
type.xml { render :text => @resource.to_xml }
type.yaml { render :text => @resource.to_yaml }
#type.json { render :text => @resource.to_json }
#残念ながらtype.jsonはまだ対応していない
end
when :put
# Updating a Resource (省略)
when :delete
# Deleting a Resource (省略)
end
else # idがなければCollection URI
case request.method
when :get
# Listing Collection Members (省略)
when :post
# Creating a Resource
new_resource = model.new(params[params[:model].to_sym])
if new_resource.save
render_post_success(:id => new_resource.id)
else
render :text => new_resource.errors.full_messages.join("\n"), :status => 406
end
end
end
end
さらにroutes.rbをこんな感じに設定。
map.connect 'rest/:model/:id',
:controller => 'rest', :action => 'resource', :id => nil
/rest/<モデル名>/<ID> という統一されたURI(Atom ProtocolでいうMember URI)でリソースにアクセスでき、さらに"Accept: application/xml"をつけるとXMLで取得、"Accept: application/x-yaml"をつけるとYAMLで取得したりできる。(JSONのMIMEタイプにまだ対応していないのが惜しいが、application/javascriptでもいいかな)
さらに取得だけではなく、/rest/<モデル名>というURI(Collection URI)に"Content-Type: application/xml"をつけてXMLをPOSTすれば、通常のFORM形式に変換してデータをCREATEしてくれる(くまくまーでは「うまく動きませんでした」って書いてあるけど、Rails 1.1.2で動作を確認。ただしContent-Lengthヘッダが必要)。
アプリケーションのすべてがRESTである必要はないが(というか無理)、Ajaxで使われるインターフェイスなどはとくにこんな感じで簡単にRESTが使えると、Webアプリケーションが即Webサービスとしても使えるようになるのでうれしい。
参考:
投稿者 4bit : 11:21 | コメント (2) | トラックバック (0)
Rails勉強会@東京 第5回 [テクニカル]
第0回以来、久しぶりにRails勉強会に参加。ポジションペーパーの内容はこちら
前半のセッションは、僕がなにげなく発した「Rails 1.1の新機能って前回やったんですっけ?」の一言からセッションオーナーになってしまい、たいした準備もないままぐだぐだのセッションになってしまった。参加してくださった皆さんすいませんでした。そしてサポートしてくださったIさん(名前間違ってたらすいません)本当にありがとうございました。内容はこんな感じ。
- Cascaded Eager Loading (ActiveRecordでは最も大きな変更点) 参考1 参考2
- 実際のアプリのDB構成では2階層以上のリレーションを持つ場合がほとんどなので、今まではSQLを書いていたが、これによって書く手間が省ける。とてもうれしい。使いまくり
- paginateで使えるの? →ソースを見てみた結果、findを呼んでるだけなので使えるはず。OFFSET, LIMITをつけたときの挙動などはSQLの問題
- Rakeコマンドのnamespaceが変更
rake -T
でタスク一覧を見てみるとコロンでつなぐ形式に変わっているのがわかる- tmp:sessions:clearとかrails:updateとかいろいろ使えそう
- Rails 1.1(というかRake 0.7)になってタスクがかなり増えている?
- 「Rakeを使いこなす」って感じで独立セッションにしてもおもしろそう
- 国際化
- 1.1でもあまり変わっていない
- ActiveHeartは使えるはずなので使いましょう
- Controller#respond_to
- 僕が個人的に注目してます(ポジションペーパー参照)
後半のセッションは、かくたにさんの「俺と一緒にRecipe 43を一緒に読まないか」。Rails Recipesという本の主にIntegration Tests関係の話題。
- Testing Across Multiple Controllers
- 今までModel(unit)とController(function)単位のテストサポートだったが、Integration Testsとしてストーリーレベルのテストをサポート
- ストーリーに沿ってテストを可読性の高い形で記述しよう。そうするとテストがもっと楽しくなるよ
- DSLマンセー
- Make your URLs Meaningful
- 名前つきルートを効果的に使おう
url_for :action => :login
のかわりにlogin_url
とか書けるので可読性がさらにアップ
終わった後は恒例の懇親会。なんとなく、みんなJavaには苦労してるんだなーと思ったり、意外な人のつながりがわかったり、出版業界の裏話とか聞けたりしておもしろかったです。みなさんありがとうございました。そして幹事の井上さんありがとうございました。
投稿者 4bit : 10:14 | コメント (0) | トラックバック (0)
2006年04月04日 (火)
Maven2のホームディレクトリ設定 [テクニカル]
そろそろJavaを使い始めなければならないのでMaven2でプロジェクトを作ってみる。昔Maven1は使っていたんだけどもうJava自体かなり忘れているのでわからないことだらけ。「Maven2のTipsを集めるWiki - CookBook」を見てプロキシなどを設定。
「nobeansの日記 - Windows環境でのローカルリポジトリ」よりデフォルトでは C:\Documents and Settings\<ユーザID>\.m2 配下なのはいいんですが、HOME環境変数を設定しても変わってくれません。なんで?
cygwinつかってるので、cygwin上での /home/<ユーザID>/.m2 にあたるディレクトリに変更したいんですけど。
激しく同感。Maven2から認識されるホームディレクトリは環境変数HOMEではなくてUSERPROFILEから取ってるらしい。ただこれを変えるのはちょっとまずそうなので、mvn.batをのぞいてみるとMAVEN_OPTSという環境変数を見てくれるらしい。よってこの値に -Duser.home=C:\path\to\cygwin\home\<ユーザID>
と入れてやれば、ちゃんと変わってくれた。
参考:
投稿者 4bit : 17:40 | コメント (0) | トラックバック (0)
2006年03月30日 (木)
TypeKey認証を使ってみる [テクニカル]
「TypeKeyによる認証 :: wiki.rails2u.com」を参考に、RailsでTypeKey認証を利用してみたのでメモ。
最初TypeKey側でエラーが出ていっこうにログインできるようにならなかったのだが、TypeKeyアカウント設定の「コメント登録するウェブログの指定」にURLを登録しなければいけなかったらしい。現在localhostで実験中なので http://localhost:3000/
てな感じで指定する。
次に、Windows環境のため /tmp/tk_key_cache がないというエラー。type_key.rbの55行目あたりをこんな感じに変更。
tempdir = ENV['TMPDIR'] || ENV['TMP'] || ENV['TEMP'] || '/tmp'
@key_cache_path = tempdir + '/tk_key_cache'
最後に、サンプルコードの redirect_top
メソッドがないエラーが出たので、 redirect_to top_url and return
に書き直して終了。動作OK。
認証とかめんどいので他に任せてしまいたい僕としてはTypeKeyはかなりうれしい。便利だなー。
投稿者 4bit : 17:49 | コメント (0) | トラックバック (0)
2006年02月28日 (火)
RailsによるアジャイルWebアプリケーション開発 [テクニカル]
買いました。英語の原著も買ってたんだけど、やっぱり日本語のほうが安心感がある。Rails1.0に対するフォローも入っていていい感じ。
Pragmatic Bookshelf (2005/09/22)
売り上げランキング: 3,522
せっかく買ったんだから、来月のリハビリ期間には何か作ってみたいなぁ。Typoっていうブログツールはよくできているので、これをいじってみるかな。今はこのブログはMovable Typeを使ってるけど、結局Rubyという言語が好きなので、Typoに移行するかも。
投稿者 4bit : 12:06 | コメント (0) | トラックバック (0)
2006年02月26日 (日)
サーバ構築挫折… [テクニカル]
友人のK君に頼まれて、K君のWindowsで稼動しているサーバをLinuxで構築し直す作業をしたのだが、10時間ほど格闘した結果、今日のところは断念することに。
原因はいろいろあるのだが、やはり運用面での不安が大きい。K君はLinuxにさほど詳しくないため、Webで設定ができるようにしようとWebminを入れたのだが、やはり使いづらいし、ちゃんと使えるようにしようとするとWebminの設定では追いつかないことも多い(SMTP-AUTHなど)。まずWebminをパッケージで入れるとユーザの認識がおかしくてtarボールから最新版を入れなければならなかったり、細かいトラブルが多かった。僕も準備不足で突っ走ってしまったのがよくなかったと思う。
ただLinuxをいじるのも久しぶりの状態で、「徹底入門」を頼りにしたが1日でWeb(Apache)、FTP(ProFTPD)、Mail(Postfix)、Sambaあたりがそこそこ使える状態までセットアップできたのは自分としてはまずまずかな、と思う。結果は残念だったが、いい経験になった。また今度は準備を整えて再挑戦したい。
翔泳社 (2005/08/04)
売り上げランキング: 1,062
分かりやすいdebianの入門書+パッケージ
リョービのフォント目当てに買ってしまいました
投稿者 4bit : 11:05 | コメント (0) | トラックバック (0)
2005年11月02日 (水)
カーネル再更新 [テクニカル]
前回のカーネル更新のとき、Firewireのモジュール周りがちょっと変だったのだが、スラッシュドットの記事“Linux 2.6.14 released”の中のコメント
sbp2 driver is not orphaned.を見てカーネルを2.6.14にしてみた。
個人的にはsbp2のドライバがまともになったのがうれしいですねえ。
2.6系はちょっと前までエラー連発で、外付けのドライブをUSBにしようかと思ってましたから…。
しかし、メンテナがいなかったとは思わなかった。
たしかにsbp2は新しくなっているみたい。今のところ様子見…。
追記:参照カウントは増えなくなったが、SCSIホストアダプタ番号が増えるのは変わっていない。設定でなんとかできそうな気もするんだが…。関係ありそうな記事“junk words - usb-storage のデバイス名/マウント”
さらに追記:sbp2モジュールを手動で取り外せばアダプタ番号もリセットされた。よかったよかったー。あとは自動でモジュール着脱するためのudevスクリプトの書き方かな。
投稿者 4bit : 23:00 | コメント (0) | トラックバック (0)
2005年10月21日 (金)
TypeKeyおかしい? [テクニカル]
「ここギコ!: OpenIDで個人サイト同士が繋がるSNS - Social_Networking_Unlimited」を見ておおっ!と思ったので、TypeKeyアカウントでログインしてみようと思って、ここのインデックスのテンプレートに
と書いてここのURLでログインしてみたが、<link rel="openid.delegate" href="http://profile.typekey.com/4bit/" />
<link rel="openid.server" href="https://www.typekey.com/t/openid" />
“The remote OpenID server reports that the login was cancelled.”
と出てきて全くログインできない。なぜ~?
投稿者 4bit : 09:00 | コメント (4) | トラックバック (0)
2005年10月20日 (木)
サーバOS更新 [テクニカル]
今さらながら、家のサーバのDebianをSargeに更新。リリースノートの手順通りでほぼ問題なくアップグレードできた。こんなにあっけないならもっと早くやっておくべきだったかな。どうでもいいけど http://www.jp.debian.org/ って落ちてる?
引き続き、カーネルを2.6.13.1に更新。こういうときに頼りになるのが「Debian GNU/Linux スレッドテンプレ」と「徹底入門」。巻末のカーネル設定項目解説はとても役に立った。ビルド手順も本の通りに進めて一応完了。
翔泳社 (2005/08/04)
売り上げランキング: 1,062
分かりやすいdebianの入門書+パッケージ
リョービのフォント目当てに買ってしまいました
更新が終わっていろいろいじっていたのだが、ちょっと問題点が…。FireWireで接続しているハードディスクとDVDドライブがあるのだが、接続すると自動認識してモジュールが組み込まれて使えるようになるのはいいが、取り外しができない。DVDの場合だと、sr_modモジュールがbusyになって取り外しができず、modprobe -rをやると固まる…。さらにハードディスクはなぜか1台しかつないでいないのにSCSI IDが0~7まで8台も認識されたり、とてもあやしい雰囲気。USBまわりは利用者が多いから整備されてるけど、FireWireは未整備なのかなーと思ったりもする。ファイルサーバも兼ねてるからこれじゃちょっと困るなぁ…。
投稿者 4bit : 22:00 | コメント (0) | トラックバック (0)
2005年07月13日 (水)
はてなブックマークは便利だけど… [テクニカル]
今まで、Webを見ていて気になった記事のURLをOutlookとかテキストファイルにメモしていたのだが、そういう用途にぴったりだと思ったのではてなブックマークを使い始めた。私のはこれ:
タグ(キーワード)も打てるし、コメントも付けられるのでとても便利。いちばんすばらしいのが、特定の記事をブックマークしている人のリストとそのコメントが見られること。ソーシャルブックマークの魅力がわかったような気がする。これで自分の記事をブックマークしている人がけっこういることがわかってうれしかった。
うれしいことが多いのだが、長めのコメントを書いてblogの記事にできそうなものもブックマークだけで済ませてしまうクセができそう。blogと統合したいなぁ。blogのエントリがもっと簡単に書ければいいのかもしれないけど。
blogを使い始めて、こういうことも含めていろいろと機能に不満な点が出てきている。いくつかはMovable Typeのプラグインで解決できるんだろうけど、もっと根本的な何かが…。整理できたらまた書きます。
追記: おもしろい意見を発見。
svnseeds’ ghoti!: はてなブックマーク論壇(笑)じゃなくてはてなブックマークのコメントが気持ち悪い件について
たぶん似たようなことは散々言われているんだろうけど、どうもはてなブックマークのコメントが気持ち悪い。自分用のメモだったら全然良いんだけどそうじゃないのは激しくアレだ。評論したいんなら自分の日記なりブログなりですれば良いのにね。
元記事のコメント欄に書かない理由は、やっぱり「パーソナルスペース」ってのが大きいと思う。他人のblogにコメントする行為って、自分のスペースから出る行為なので、心理的障壁が大きい。例えパブリック(公開されている)でも、「自分のスペース」にコメントが書けるってことが大きいメリットなわけだ。あとblogに書くのよりブックマークしてコメント書くほうがとても楽だからっていうのもある。私の場合はこっち。
評論の一方通行がイヤというのは同感。でも改良案にはあまり賛成できない。プライベートコメントというのはソーシャルブックマークの方向性にも反するだろう。問題は、コメントをWebとしてつなげる方法がないということではないだろうか。そこで一案としては、コメントにPermalink割り当ててトラックバック打てるようにすればいい。つまりblogのエントリと同レベルで扱えるようにする。結局上で言っているように「blogと統合」みたいな話になるんだけど。
関連の議論はけっこうあるみたい。
mjw-a.net(愚痴ブログ): はてなソーシャルブックマークは、カウンターを打たせる隙を作るべき
『斬(ざん)』:ホントにそれブックマークしたいのかなぁ~
活動メモ 2nd season - ブックマーク雑感
投稿者 4bit : 10:15 | コメント (0) | トラックバック (0)
2005年07月06日 (水)
Atom + OpenSearch [テクニカル]
昨日のAtomPPの検索のつづき。YAMAMOTO Yohei氏の関連記事を読み進めながらいろいろ調べてみると、A9 OpenSearchに話がつながることがわかった。やっと数ヶ月遅れで話題に追いついたのかな。yohei氏の記事ではAtomフィードを拡張してOpenSearch RSSを追加する例が示されている。
でもOpenSearchってなんか方向性が違う気がするんだけど。検索エンジンのインタフェース統一なんて高尚なものではなくて、AtomがPublishing ProtocolとしてCRUDを実装するなら当然検索も必要です、と言いたいだけなのだ。CRUDのRは、ReadだけではなくてRetrieveもあるのです(と、yohei氏の受け売り)。
よって重要なのはクエリ書式を決めるDescription Documentのほう。で、どうやら期待通りQuery Syntaxで searchTerms, count, startIndex, startPage の4つが定義されているようだ。これで一応クライアントによるシンプルな検索も可能になる。しかしこの程度の仕様はAtomに入れてほしかったなぁ…。OpenSearch Description Documentの書式はあまり整っているとは思えないし、Atom FeedURIの提示と重複してしまっているという問題点があるから。
追記:
IMCのMLに全く同じようなスレッドを発見。今少しずつ読んでます。その中からデメリットの指摘など。
http://www.imc.org/atom-syntax/mail-archive/msg15876.html
Search results are expected to be returned in the OpenSearch RSS format.
http://www.imc.org/atom-syntax/mail-archive/msg15877.html
We were also a little concerned that the OpenSearch model was very simplistic - there were many possible use cases we could think of that weren't catered for, and indeed we could only think of one that was really supported, being the one that A9 puts it to (which is a slim superset of feeding OpenSearch into a desktop aggregator).
昔のAtomAPI (draft-gregorio-07)にはちゃんと検索仕様があったんだねぇ。
投稿者 4bit : 13:43 | コメント (0) | トラックバック (1)
2005年07月05日 (火)
AtomPPに検索って無いの? [テクニカル]
AtomPP (Atom Publishing Protocol; 今までは Atom API と呼ばれていた)の仕様ドラフトを読んでいたのだが、検索機能が規定されていない気がする。
具体的には、まずRetrieve用URIの規定がない。FeedURIを使うと思われるが、そもそもFeedURIの意味が広すぎる。
さらに、GETメソッドでのクエリーの書式も規定されていない。これが決まっていないと、クライアントはどうやって目的のエントリを検索したらいいのかわからない。
これってとてもマズイ気がするんですけど…。
追記:
はてなのアプローチなど、たしかにこのようにすればRESTにしたがって検索機能を実装することが可能。しかし、問題なのはこの機能をクライアントが知り得ないという点だ。はてなのFeedURIにはクエリパラメータof, tag, word...が使えるということは、仕様を見ない限りわからない。オフセットや検索語などかなり汎用的なものであるにもかかわらず。
これをなんとかするためには、フィードの中のFeedURIを示すlink要素に、利用可能なクエリパラメータとその意味をある程度統一的な方法で示すことが必要だと思う。
関連記事:yohei-y:weblog: REST 入門(その6) ハイパーリンクと XML
Atom解説
http://www.witha.jp/Atom/
The Atom Publishing Protocol (draft-ietf-atompub-protocol-04)
http://www.ietf.org/internet-drafts/draft-ietf-atompub-protocol-04.txt
The Atom Syndication Format (draft-ietf-atompub-format-09)
http://www.ietf.org/internet-drafts/draft-ietf-atompub-format-09.txt
投稿者 4bit : 19:30 | コメント (2) | トラックバック (0)
2005年06月23日 (木)
RESTfulアプリケーションとCookie [テクニカル]
おおっ、RESTful Application関連のレポートを発見。しかも2年前。
方向性はおおむね同意。そして、クッキー使ってもいいんじゃない? たしかにパフォーマンス低下やプライバシー漏洩ってのはあるけど、RESTの原則からいうと、クッキーによってresponseを変えてはならない、ってだけだから、クッキーを手軽なローカルレジストリとして使うのはアリなような気がする。ドメイン別だから便利だし。もちろん、都度サーバに送られたクッキーは使わない。
それとも、ブラウザが開いている間はオンメモリで保持しておいて、ブラウザが閉じる(もしくは別のサイトに移る)ときに、ローカルのクッキーに書き出すっていうことはできないかな。そしてまた開いたときにクッキーから読み出す。そうすれば不要なデータは流れなくなる。
もっというと、クッキーに依存してresponse変えちゃってもいいんじゃないかと思う。広い意味のContent Negotiationとみる。つまり、RESTはURIとHTTP Methodによってリソースが決定されるが、Accept-Languageなどのヘッダによるリソース選択は認めている(と思う、たぶん)。ただしリソースの意味が変わらない範囲において。ならば、クッキーのデータもリソース選択の一要素と認めてもいいんじゃないか、という考え。これはサーバで状態を保持しているわけではないので完全にStatelessである。
ちょっとまだ考えが浅いかも。というか論文とかこのへん読めば答えが書いてあるような気がするのだが、英語読むのめんどい…。その気になったら読むことにしよう…。
追記: 上記の「REST版ショッピングカート」と同じ市山氏のレポートより、クッキーがRESTに合わない理由。
「クッキーで反映される前のビューを保存するためにブラウザの履歴機能が使われると、ブラウザーのアプリケーション状態はクッキーに保存された状態と一致しない
」
確かに。でもこれじゃコンテントネゴシエーションもNGになってしまわないか? うーむ。
投稿者 4bit : 15:36 | コメント (40) | トラックバック (1)
AnnotationとPOJO [テクニカル]
『設計と実装の狭間で。 - AJOってどうよ?』を読んで。ちょっと極端な考え方かもしれませんが。
POJOという言葉が、現在どういう意義・意図を持って使われているのかが重要。言葉の発祥としてはどうやらEJBのカウンター的概念として作った言葉らしい(Martin Fowler's Blikiなど参照)。よって複雑さの解消という意義ももちろんあるだろう。しかし、現在では主に「実装依存性の解消」という意義がクローズアップされていると考える。これはTDDやDIにもつながる考え方である。
簡単な例だと、「ライブラリのような外部の特定クラスからの継承をしない」というのがある。クラスの継承は基底クラスへの依存性を生む。つまり外部のライブラリの実装に強く依存したクラスとなってしまうからだ。だからといって、継承しないといっても、極端な例を挙げると、
class Foo
private bar;
public Foo() {
bar = new BarImpl(); // BarImplは外部ライブラリの実装クラス
}
...
}
こんなことをしていると完全にダメだ。BarImplという特定の実装クラスに依存しているからである。これでは結局testabilityが失われ、POJOとしての意義をなさないのではないかと考える。ここまでくるとPOJOという言葉がふさわしいかどうかわからない。だがそういう意義をもって使われている言葉だと私は理解している。
本題。Annotationをつけることは、実装依存性と全く関係がない。「オブジェクトが期待される全ての機能」とは、クラスにコードとして書かれているものがすべてであって、Annotationによって実現される付加的機能はあくまでAnnotationを処理する外部のライブラリによって実現されるのである。そしてそれらの間には(強い)依存性はない。よって、AnnotationをつけたクラスのオブジェクトはPOJOと呼んで全く差し支えない。
投稿者 4bit : 11:54 | コメント (0) | トラックバック (0)
2005年06月21日 (火)
RESTful Web Applicationの可能性 [テクニカル]
RESTについて、前に先輩のsatoshi氏と話をしていたのだが、共通の感想が「WebブラウザからPUT, DELETEメソッドが使えればいいのに…」ということだった。HTML form要素のmethod属性は、仕様として"GET"と"POST"しか許されていない。
そこでPUT, DELETEなしでWebアプリケーションのURI設計をするとしたらどうなるかなと思い、ちょっと調べてみると、W3CでXFormsという規格があり、他のメソッドも使えるようになっている。XFormsは複雑すぎるということからWeb Forms 2.0という規格も提案されている。
そういえば、最近流行りのAjaxのXMLHttpRequest使ったらできるんじゃないの?
と思って、適当なhtmlファイルを作って試してみたら、見事にIEとFirefoxの両方でPUTもDELETEも成功!(任意のメソッドOK) すばらしい。スクリプト必須ではあるが、現時点でもRESTfulアプリケーションの可能性が見えてきた。
参考リンク:
HTML 作成者のための XForms 入門
Web Forms 2.0 Working Draft
ちなみに作ったhtmlはこんなの。
<script language="javascript" type="text/javascript">
function createXMLHttpRequest() {
return this.XMLHttpRequest ?
new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
}
var req = createXMLHttpRequest();
function send_request(method) {
req.open(method, "httpmethod.html", true)
req.send(null)
}
</script>
<input type="button" value="GET" onclick="send_request('GET')"/>
<input type="button" value="POST" onclick="send_request('POST')"/>
<input type="button" value="PUT" onclick="send_request('PUT')"/>
<input type="button" value="DELETE" onclick="send_request('DELETE')"/>
なぜかIEでGETとPOSTだけ成功しなかったのだが、たぶんコードの書き方がどこかおかしいのだろう。GETとPOSTなら普通のフォームでできるし。
Web ApplicationをRESTfulに近づけていくことで、Web Serviceとの垣根をなくす方向を目指したい(すでに一部のBlogシステムはそれに近い)。そのためにはRESTを意識したWeb Application Frameworkが必要。
アプリケーション内部のデータ(コンテンツ?)をすべて統一的な形(XML?)で定義できるようにし、すべてをリソースとみなしてURIを与える。プログラム内でも基本的にそのリソース識別子によってアクセスする。
すべてのデータがXMLもしくはHTMLで定義できるのであれば、それをプログラムで直接扱える形としてのオブジェクトと相互変換することが必要。Object-XMLマッピングの領域。O/R Mappingの技術に近く、実装にアノテーションなどの技術が使えそう。概念的にCMSに近いものになってきそうな気もする。
しかし、Roy Fielding曰く、『Web がうまく機能したのはシステムのインターフェースに object-specific な属性を採用しなかったことにあると僕は考える。だからマーケティングキャンペーンが単にそう言っているからといって、object-specific な属性がWebアーキテクチャの一部になることを許すのは、バカげているだけでなく、私たちが懸命に作ろうとしているシステムの未来に反している』。
んー、つまりWebの世界をOOでラップするのはやめれってことじゃないのかなぁ。僕の言っているのは、レイヤーの境界ではいちいち変換しろってことだから、Fielding氏の主張には反してないと思うのだが。
ほんとうは元の論文を読むべきなんだけど、英語なのでちょっとつらい。。。
メモ:「リソース指向」と「アクティビティー指向」という言葉を使って相違点を探る記事。
http://www-6.ibm.com/jp/developerworks/webservices/041119/j_ws-restvsoap.html
投稿者 4bit : 15:27 | コメント (0) | トラックバック (0)
2005年06月20日 (月)
LAMP [テクニカル]
いつのまにかLAMPのPが、PHPだけじゃなくてPerlとPythonも含む事になってる。えらい拡大解釈じゃないか?と思ったらWikipediaの解説でもそうなってた。もしかして頭文字がPじゃないという理由だけでRubyは仲間はずれですか? (LAMPのP)
同じこと思ってた…。LAMPってPHPだろ~。というかRubyだけ無意味に疎外されてる感じがしてイヤだなぁ。僕の中ではPerlとPHP、PythonとRubyが近いグループだと思ってるんだけど。そして前者はステ。
投稿者 4bit : 19:50 | コメント (0) | トラックバック (0)
2005年06月10日 (金)
metadataライブラリ [テクニカル]
[ruby-talk:93813] Re: Extensible meta-data ? に倣って、メタデータ処理ライブラリを書いてみた。ただし、クラスへのメタデータ付加は必ずselfを指定するようにする。やっぱりちょっとダサいけどしょうがないか。。。
使い方の適当な例は以下。ハンドラの設定部がポイント。
require 'annotation'
MetadataInterfaceName.def('META')
# ハンドラの例…属性を定義する
# ハンドラ設定は、メタデータ記述の前に行わなくてはいけない点に注意
# (ただし、定数アノテーション対応のため、記述後からのハンドラ起動機構も設ける予定)
MetadataHash.set_handler(:Attribute) do |owner, elem, data|
case data
when :reader, :writer, :accessor
owner.__send__(:public)
owner.__send__('attr_' + data.to_s, elem)
end
end
class Foo
META self, :Entity, :Table => 'foo'
def foo
puts "bar"
end
META :foo, :Method => "metadata", :Role => "bar"
META :hoge, :Attribute => :accessor, :Role => "bar2"
end
p Foo.metadata
# => {[:hoge, :Role]=>"bar2", [Foo, :Table]=>"foo", [:foo, :Method]=>"metadata",
# [:hoge, :Attribute]=>:accessor, [:foo, :Role]=>"bar", [Foo, :Entity]=>true}
p Foo.metadata.fetch_element(:foo)
# => {:Method=>"metadata", :Role=>"bar"}
p Foo.metadata.fetch_name(:Role)
# => {:hoge=>"bar2", :foo=>"bar"}
p Foo.instance_methods - Object.instance_methods
# => ["hoge", "foo", "hoge="]
ちょっと長いけどソースはこちら。
class DoubleKeyHash < Hash
def initialize(ifnone = nil, &block)
super
@key1key2 = {}
@key2key1 = {}
end
def [](*keys)
if keys.size == 1 and keys[0].kind_of?(Array)
super(keys[0])
elsif keys.size == 2
super(keys)
else
raise ArgumentError
end
end
def []=(*args)
if args.size == 2
raise ArgumentError unless args[0].kind_of?(Array) and args[0].size == 2
add_keys(args[0][0], args[0][1])
double_key_store(args[0][0], args[0][1], args[1])
elsif args.size == 3
add_keys(args[0], args[1])
double_key_store(args[0], args[1], args[2])
else
raise ArgumentError
end
end
def delete(key, &block)
super
remove_keys(key[0], key[1])
end
def update(other, &block)
raise TypeError unless other.kind_of?(DoubleKeyHash)
@key1key2.update(other.instance_eval('@key1key2'))
@key2key1.update(other.instance_eval('@key2key1'))
super
end
def fetch1(key1)
ret = {}
@key1key2.fetch(key1, []).each do |key2|
ret[key2] = fetch([key1, key2])
end
ret
end
def fetch2(key2)
ret = {}
@key2key1.fetch(key2, []).each do |key1|
ret[key1] = fetch([key1, key2])
end
ret
end
def double_key_store(key1, key2, value)
store([key1, key2], value)
end
private
def add_keys(key1, key2)
@key1key2[key1] ||= []
@key1key2[key1] << key2
@key2key1[key2] ||= []
@key2key1[key2] << key1
end
def remove_keys(key1, key2)
if @key1key2.has_key?(key1)
@key1key2[key1].delete(key2)
@key1key2.delete(key1) if @key1key2[key1].empty?
end
if @key2key1.has_key?(key2)
@key2key1[key2].delete(key1)
@key2key1.delete(key2) if @key2key1[key2].empty?
end
end
end
class MetadataHash < DoubleKeyHash
@@handler = {}
@@default_handler = nil
attr_reader :owner
def initialize(owner_class)
super()
@owner = owner_class
end
def double_key_store(elem, name, data)
super
if @@handler.has_key?(name)
@@handler[name].call(owner, elem, data)
elsif @@default_handler
@@default_handler.call(owner, elem, name, data)
end
end
def update(other, &block)
raise TypeError unless other.kind_of?(MetadataHash)
super
# handler calling is still not supported
end
def fetch_element(elem)
fetch1(elem)
end
def fetch_name(name)
fetch2(name)
end
def self.handler
@@handler
end
def self.set_handler(name, &block)
@@handler[name] = block
end
def self.default_handler
@@default_handler
end
def self.set_default_handler(&block)
@@default_handler = block
end
end
module MetadataInterface
def meta(elem, *values)
md = __send__(MetadataInterfaceName.metadata)
values.each do |value|
if value.kind_of?(Hash)
value.each do |name, data|
md[elem, name] = data
end
else
md[elem, value] = true
end
end
end
def metadata
@metadata ||= MetadataHash.new(self)
end
end
class MetadataInterfaceName
# 名前の再定義
# MetadataInterfaceName.def('META') のように用いる
@@meta = :meta
@@metadata = :metadata
class << self
def meta
@@meta
end
def metadata
@@metadata
end
def def(name)
MetadataInterface.__send__(:alias_method, name, @@meta)
MetadataInterface.__send__(:remove_method, @@meta)
@@meta = name
end
def def_data(name)
MetadataInterface.__send__(:alias_method, name, @@metadata)
MetadataInterface.__send__(:remove_method, @@metadata)
@@metadata = name
end
end
end
class Module
include MetadataInterface
end
ちなみに、metadata定義をC#のattribute流儀の [...] にできないかと思って
MetadataInterfaceName.def('[]')
とやってみたが、[]はselfをつけないと配列リテラルとみなされるためNG。ざんねん。
投稿者 4bit : 19:30 | コメント (0) | トラックバック (0)
2005年06月09日 (木)
Rubyにannotationを定義するとしたら? [テクニカル]
きのうのエントリはちょっと意味不明だったが、結局言いたいことは「Rubyにアノテーションを定義するとしたら、どのような書式がよいか?」ということだ。
同じことを考えている人はいるもので、Annotations in Rubyではチョチョイと1時間でハックしたよ、というコードが載っている。うん、これぐらいシンプルでいいのだ。こういうコードならいくらでも書ける。
で、問題はアノテーション定義の書式。上の場合では
ann :description => "IP address of the mail server", :tip => "Use 'localhost' if you have a good box, sister!"
attr_accessor :server
と使うようだが、直後の属性だけに適用するという規則がどうも不自然。やはりModuleクラスのメソッドを使うならば、publicなどと同じく「引数なしのときは今後このクラスまたはモジュール定義内で新規に定義されるメソッドに同一アノテーションを設定し、引数が与えられた時には引数によって指定されたメソッドにアノテーションを設定する」というのがRuby流儀と考えるのが自然かなぁ。
ってことで、いろいろ考えてみたが、いいアイデアが思いつかず。アイデア求む。
投稿者 4bit : 18:08 | コメント (0) | トラックバック (0)
2005年06月08日 (水)
annotation (a.k.a. metadata) 再考 [テクニカル]
Rubyの attr_*
はannotationにほかならない。内部ではメソッド定義でしかないにもかかわらず、「属性」という意味づけが与えられているのだから(RDocなど)。つまり、メソッドに attribute というannotationをつけていることに等しい。
で、Rubyで統一的な annotation を与える書式はどうするのがいいだろうか。やはりRubyでは可読性の高いModuleクラスのメソッドとして定義するべきなのか。
実は暗黙のうちにmetadataを使っているライブラリは多い。
e.g. StrongTyping module:
attr_accessor_typed String, :foo, :bar
うーん、ダサい。
metadataの意味では、foo, barというメソッドに attribute=>readwrite, type=>String というmetadataを与えていると解釈できる。ならちゃんと並列に同じ記法で書けなきゃ。
Java流なら:
meta_attribute :RW
meta_type String
meta_hoge :key1=>'value1', :key2=>'value2'
def foo
...
end
って感じか。ちょっとダサいかな。。あと意味もわかりにくい。meta_*
っていうメソッド名もかぶりそうだし。
もう1つの例。
e.g. ActiveRecord
class Entry < ActiveRecord::Base
def self.table_name; entry; end
belongs_to :blog
belongs_to :author, :foreign_key => 'entry_author_id'
has_one :trackback, :foreign_key => 'trackback_entry_id'
has_many :comments, :foreign_key => 'comment_entry_id'
has_and_belongs_to_many :tbpings, :finder_sql => 'SELECT t.* FROM mt_trackback j, mt_tbping t WHERE t.tbping_tb_id = j.trackback_id AND j.trackback_entry_id= #{entry_id} ORDER BY t.tbping_id'
end
metadata的解釈では、クラスEntryにいろいろmetadataをつけてることになる。Java的アプローチだとこれらはメソッドにつくことになる。さらにオプション項目があればネストしたアノテーションとなる。
メソッドにつけたほうがきめ細かく処理できるはずだが、Rubyではメソッドも自動生成することが多いため、うまい方法がない。必ずdefを書くようにすればできそうだが。(それでもメソッド定義のフックはModule#method_addedで可能だが、クラス定義のフックはどうする?)
Javaで書くとするとこんな感じ?書いたことないから適当だけど。
@Entity
@Table("entry")
public interface Entry {
@ManyToOne
public Blog getBlog();
@ManyToOne
@JoinColumn(name="entry_author_id")
public Author getAuthor();
@OneToOne("trackback")
@JoinColumn(name="trackback_entry_id")
public Trackback getTrackback();
@OneToMany("comment")
@JoinColumn(name="comment_entry_id")
public Set<Comment> getComments();
@ManyToMany("tbping")
@AssociationTable( ... )
public Set<TBPing> getTBPings();
}
投稿者 4bit : 19:18 | コメント (0) | トラックバック (0)
2005年06月07日 (火)
AmritaHandler(仮) 20050607版 [テクニカル]
変更点は、定数アノテーションをスキャンする部分を独立にクラス化。まだ汎用的にはなってないけど。さらにそれを使ってNeedle登録を自動化。アノテーション INSTANCE, INIT_PARAM が使える。何も書かなければsingletonで登録される。
また、前回作ったTodoアプリケーションをActiveRecordでも実際に実装してみた。一応動いたけどチェックボックスがまだちゃんと処理できてない。これはモデル上 true, false で扱っているものをデータベース上では整数値(1, 0)で扱っているミスマッチのため。でもこれってActiveRecordが面倒見てくれてるはずなんだけど…。
todo.rbのコメントにActiveRecordを使う場合のTodoクラスのコードが書いてある。実行するにはActiveRecordとSQLite3/Rubyが必要だが、RubyGemsをインストールしているなら、 gem install activerecord; gem install sqlite3-ruby でOK。もちろんMySQLなどの他のデータベースも使える(はず)。
ActiveRecordについてはRubyist Magazineの「RubyOnRails を使ってみる 第 3 回 ActiveRecord」の記事が詳しいです。
download amritahandler_20050607.zip
投稿者 4bit : 18:50 | コメント (0) | トラックバック (0)
2005年05月27日 (金)
AmritaHandler(仮) 20050526版 [テクニカル]
Ruby on RailsのマニュアルにあるTodoアプリケーションをAmritaHandler風に作ってみた。modelはActiveRecord風に実装。実際にActiveRecordを使うことも可能なはず。
しかし、調子に乗ってmodelにアノテーションをつけたらmodelが特定のviewに拘束されるというひどいことになってしまった(当然と言えば当然だが)。これは悪い例ということになるが、かといって全メソッドをmodelに委譲するコードを書くのも面倒。やはり設計に問題があったか…。modelとviewは当然切り離されていないといけないので、helperにアノテーションをつけるという方向性になるか?せっかくいいところまできているので、当初のアイデアを崩さないように改造していきたい。
Amrita Rails Bridgeはかなりシンプルな構成なので、まずはこちらに対応させるのを先にするという手もある。
前からの変更点は多すぎるので割愛。仕様はさほど変わってない(はず)。
amritahandler_20050526.zip download
動作にはAmrita2 1.9.4とNeedle 1.2.0が必須。また、標準ではwebserver.rbにRubyGemsを使うようになっているが、使わない場合は require 'rubygems' を削除して、require_gem 'needle', '>= 1.2.0' を require 'needle' に書き換えればOK。
投稿者 4bit : 11:21 | コメント (0) | トラックバック (0)
2005年05月17日 (火)
REST [テクニカル]
Web Services Considered Harmful?(Webサービスは有害か?)
うーん刺激的。
寡聞ながらRESTというのを初めて聞いた。まったく俺が言ってたことと同じじゃん!(ってのはちょっと偉そう)
これを読むべし。まだ途中だが、かなり重要なことが書いてある。今のWebアプリケーションのほとんどがダメダメな理由はまさしくこれ。もちろんRESTを完璧に守ることは不可能だが、その考え方を積極的に取り入れていくべきだ。
投稿者 4bit : 21:25 | コメント (0) | トラックバック (0)
2005年05月14日 (土)
J2EE勉強会 [テクニカル]
“J2EE Development Without EJB”読書会の流れを受けた勉強会に初参加。今回のテーマは
- S2JTA、S2DBCPのソースを読んで、JTAとコネクションプーリングについて理解する
- JSFを基礎から応用まで理解する。第1回は、JSF VS Struts
ということで、Seasar2のひがやすを氏が解説してくださった。
JTAとコネクションプーリングは、Seasar2のシンプルな実装を見ることで基本的な考え方がわかりやすかった。予習せず臨んだのでトランザクションは予備知識が足りなかったが、ポイントはつかめたと思う。
目当てのJSFは、時間が押して短くなったがかなりうれしい内容。ところどころS2JSFの実装例を紹介しながらJSFの全体の流れやExtension Pointが解説された。これは次回以降がかなり楽しみだ。
終わった後は飲み会。初めて参加するので少し緊張していたが、同じく初めての方もいて、楽しい時間を過ごすことができた。
投稿者 4bit : 23:55 | コメント (0) | トラックバック (0)
2005年05月12日 (木)
AmritaHandler(仮) 20050512版 [テクニカル]
Amrita2が1.9.3から1.9.4にバージョンアップしてだいぶいろいろ変わっているので、対応できるように修正。前回からの変更点は…
- HTML select/optionを実装
テンプレートに書いたoption項目をそのまま生かす静的select、モデルから項目を生成する動的selectの両方を実装。ただしmultiple selectには未対応。 - HTML input type="radio"を実装
上に同じく静的radio、動的radioの両方を実装。 - ATTRアノテーション詳細化
アクション用アノテーション ATTR_ACTION_TEXTBOX, ATTR_ACTION_CHECKBOX, ATTR_ACTION_SELECT, ATTR_ACTION_SELECT_DYNAMIC, ATTR_ACTION_RADIO, ATTR_ACTION_RADIO_DYNAMIC を新設。ページ用アノテーション ATTR_TEXTBOX, ATTR_CHECKBOX だけ作成。他も作る予定。
amritahandler_20050512.zip download
動作にはAmrita2 1.9.4とNeedle 1.2.0が必須。
基本の構想はだいぶ形になってきた気がする。今後は機能を追加しつつ、最大勢力のRuby on Railsに摺り寄っていこうかと考え中。
以下適当な説明。
サンプルのlist.htmlは、マスターメンテ系を意識していろいろ詰め込んでみました。もちろんそのままブラウザで確認できます。対応するモデルオブジェクトのlist.rbはこんな感じ。
class List
attr_reader :items
def initialize
item1 = ListItem.new
item1.input = 'foo'
item1.options = [ {:text=>'hoge1', :value=>'hoge1'},
{:text=>'hoge2', :value=>'hoge2'} ]
item2 = ListItem.new
item2.input = 'bar'
item2.options = [ MenuOption.new('hoge3'), MenuOption.new('hoge4') ]
@items = [item1, item2]
end
end
class MenuOption
attr_reader :text, :value
def initialize(value)
@text = value
@value = value
end
end
class ListItem
attr_accessor :delete, :input, :select1, :select2, :radio1, :radio2
attr_writer :options
*ATTR_ACTION_CHECKBOX = :delete
*ATTR_ACTION_TEXTBOX = :input
*ATTR_ACTION_SELECT = :select1
*ATTR_ACTION_SELECT_DYNAMIC = { :name=>:select2, :item_id=>:select2option, :items=>:options }
# :text=>:text, :value=>:value はオプション
*ATTR_ACTION_RADIO = :radio1
*ATTR_ACTION_RADIO_DYNAMIC = { :name=>:radio2, :item_id=>:radio2input, :items=>:options }
def initialize
@delete = false
@input = ''
@select1 = '2'
@select2 = ''
@options = []
@radio1 = 'B'
@radio2 = ''
end
end
定数アノテーションがかなり増えました。意味は名前でなんとなくわかってもらえると思います。 ATTR_ACTION_SELECT
とATTR_ACTION_SELECT_DYNAMIC
の違いですが、前者の場合list.htmlにoption要素がすべて書いてあって、それをそのまま表示します。後者の場合はhtmlにはテンプレートとなるoptionが1つだけ書いてあり、アノテーションの:itemsに指定された名前のプロパティからoption項目を動的に生成します。データはHashと独自クラスのどちらも使えます。
ロジックはActionクラス(listaction.rb)に書きますが、マスターメンテ系なのでロジックはあまりありません。データベース連携などの機能はまだありませんが、Actionクラスに少し書き足すだけで簡単にできるでしょう。
投稿者 4bit : 21:56 | コメント (0) | トラックバック (0)
2005年04月18日 (月)
AmritaHandler(仮) つづき [テクニカル]
ちょこちょこ手を加えている。今の方針としては、Amrita2のテンプレートスペックを自動的にうまい感じに生成する方向。
TODO:
- HTML select/optionの実装
amrita2のドキュメントを見ていたら、amrita:type属性なんてものがあった。。。これを使うと少し楽にできるかも?…と思いきや、動かない。未実装のようだ。1.9.3では未実装。1.9.4で実装されたようだ。
テンプレートスペックいじりの方針で、実装の方法は見えてきた。今50%ぐらい? - ATTRアノテーションを増やす
ATTR_PARAM(リクエストパラメータ)、ATTR_ACTION(アクション)などアノテーションを書かなければすべて自動でセットされて動くが、書いておけば書いたものしかセットされない。リクエストパラメータはクライアントから勝手にいじれるので、書いたほうが無難。ACTIONは必須か?
ATTR_ACTION実装完了。 - FileHandlerの実装に依存して、実際に存在するrbファイルのリクエストしか受けられないのを直す。→というかそもそも拡張子をなんとかする。
- rbファイルの読み込みを自動にする。
いろいろ見ていたら、Ceriseというものを発見。なんかめっちゃ似てるような気が…。DIコンテナまでは使っていないようだけど。
投稿者 4bit : 00:48 | コメント (0) | トラックバック (0)
2005年04月16日 (土)
URLの重要性 [テクニカル]
このblogに使っているMovable Type、再構築したらエントリのPermanent LinkのURLが変わっちゃった…。今までのトラックバックがぁぁ~(古いURLでも残ってるっぽいけど、更新されない?) どこがPermanent[永久の、不変の]やねん。
そんなことはMovable Typeの常識なのかもしれないけど、URL(URI)ってどれほど重要なものなのか、安易に変えてはいけないものなのか、わかってないシステムが多すぎる。これを読めっ!
The Web KANZAKI 「クールなURIは変わらない」
各方面のかたへ、トラックバック打ち直すかもしれないけどよろしくお願いします。ご迷惑をおかけします…。
投稿者 4bit : 01:33 | コメント (0) | トラックバック (0)
2005年04月14日 (木)
AmritaHandler(仮) [テクニカル]
RubyにはAmritaというすばらしいHTML/XMLテンプレートエンジンがあるのだが、これをS2JSF風に簡単に使えるようにしてみよう~という発想で、WEBrickを使ってWebアプリケーションフレームワーク(というほどのものではないのだが)を作ってみた。
とりあえずWEBrickのFileHandlerのサブハンドラとして作ったので、AmritaHandler(仮)という名前。
amritahandler_0413.zip download
動作にはAmrita2 1.9.4
以下、少し説明。
S2JSFと同じようなサンプルのadd.htmlとlist.htmlがあります。まだHTMLではinputにしか対応してなくて、select/optionは実装中。
対応するRubyのソースは見てくれればわかりますが、すごくシンプルです。たとえばlist.rbはこんな感じ。
class ListItem
attr_accessor :delete, :input
ATTR_ACTION_INPUT = [:delete, :input]
def initialize
@delete = ''
@input = ''
end
end
class List
attr_reader :items
def initialize
item1 = ListItem.new
item1.input = 'foo'
item2 = ListItem.new
item2.input = 'bar'
@items = [item1, item2]
end
end
ポイントは定数アノテーション ATTR_ACTION_INPUT = [:delete, :input]
の部分です。これを書いておくとdeleteとinputがHTMLのINPUT要素と認識され、適切に値が埋め込まれます。Seasar2からアイデアをパクりました(笑)。
そしてActionを担当するlistaction.rbはこんな感じ。
class ListAction
def initialize(list)
@list = list
end
def update
@list.items.reject! { |item| item.delete == 'on' }
end
def addrow
@list.items << ListItem.new
end
def action
'list'
end
end
どちらも非常にシンプルで、しかも全く他のモジュールに依存していないことがわかります。ロジックはこれだけです。あとはwebserver.rbの中でDIコンテナであるNeedleにコンポーネントの設定をする(別ファイルに分離予定)だけで動きます。
投稿者 4bit : 01:09 | コメント (0) | トラックバック (0)
2005年04月04日 (月)
WebアプリケーションフレームワークとWebの特性 [テクニカル]
引き続きS2JSFを使っているが、JSF(MyFaces?)のid属性とname属性を勝手に書き換えてしまう仕様にはどうも納得がいかない。id属性なんて、HTML上で要素を特定するための最も重要な属性で、JavaScriptにも必須だというのに、何を考えてこんな仕様にしたのかまったく理解できない。
そう、対象となるレコードは同じなのに自動的に挿入される_id123:_link_hidden_=_id123:_id157_6:_id186って部分がどんどん変わってっちゃう。これだと?の引数で機能する動作がすべてダメになるので、ブックマークした場合に後でレコード数が変化したらもう復元できない。あと、URLの表記自体もEdit画面なのにList.htmlが表示されるという名が体を表わさないのがやっぱしダメ。(「S2JSF(MyFaces)引退しようかな。」より)
このエントリにまったく同感。今までのWebアプリケーションフレームワークは、URLをないがしろにしすぎじゃないかと思う。Webの特性をまったく無視しているというか、今までのローカルアプリケーションのような動作を再現できたらそれでOK、というような考えが見える。そういう要求にはWebは使いにくいに違いないので、Flexなどのリッチクライアント方面へ行ってくれればいい。Webで実装するからにはWebの特性を生かしたものにしないと意味がないし、その実装を補助するためにフレームワークがあるのだから。
このような考えは昔からあったものだと思うが、近年リッチクライアントが盛り上がってきたのでようやく堂々と言えるようになったような気もする(ちょっと弱気)。
投稿者 4bit : 16:05 | コメント (0) | トラックバック (0)
2005年03月30日 (水)
S2JSF試用 [テクニカル]
S2JSFを試用している。いろいろとわかりにくいところはあったが、慣れると非常に楽にWebアプリが作れる。しかしいくつかバグらしきところが…。
- input type="hidden"要素へのm:value属性の挿入にときどき失敗
リンクをすべてactionによって遷移するように変更したら失敗しなくなった(ようだ)。でもせっかくのHTMLなのに単なるリンクで遷移しちゃダメっておかしくない? バグでしょ。 - m:rendered属性のついた要素にはさまれたActionが呼び出されない
これやばい。MLでは解決したみたいなことが書いてあるけど、手元ではぜんぜん解決してないよ。これが使えないとHTMLの記述の条件分岐ができないのでダメダメ。
時間があれば再現性を確認して報告します。
追記:
どうも症状の再現性が確認できない…。時間があまりないというのもあるけど、それ以外にもパラメータが正常に挿入されなくなったり、よくわからない現象が続出。Mavenを使っているから依存ライブラリのバージョンが違うとかいうオチか?一応MyFacesについてはS2JSF添付のものに変えてみたけど症状変わらず。
もう業務に投入してるという人がいる反面、こっちの症状はなんなんだろう…。なんか根本的な環境の問題(こちら側の)があるような気がしてきた。
投稿者 4bit : 20:54 | コメント (0) | トラックバック (0)
2005年03月23日 (水)
NULLとUNKNOWNを積極的に活用するSQLの書き方 [テクニカル]
一般的に,SQL の発行は,プリペアドステートメントを使う方がよいとされている.その理由は,
- DBMS のキャッシュが良く効く.
- SQL インジェクション等の危険が避けられる,等.
しかし,webアプリの検索フォームなどでは,プリペアドステートメントは使えない場合が多かった.検索条件項目が複数存在していて,いずれの項目も必須で無い場合,項目が入力されているかどうかをチェックし,それに合わせてSQL 文の WHERE 句の内容をツギハギしなければならないからだ.
SQL文そのものも,そしてそれにセットする引数の個数も可変なので,プリペアドステートメントを使うのは無理だったのである.
今回思いついたのは,このような条件で,プリペアドステートメントを使う方法である.
...
新・たけぞう瀕死の日記より。
ほへー。目からウロコ。3値論理っていうのはおもしろいね。今まで、NULLってうぜーとしか思っていなかったが、“NULL = UNKNOWN もしくは N/A”という意味の論理値の1つとしてしっかり意識するのは非常にいいことだと思う。ただ、ちょっとめんどくさいけど。
というか、巷のデータベースの本に3値論理について書いてある本ってあったかな? もしかして常識?
投稿者 4bit : 20:11 | コメント (1) | トラックバック (0)
2005年03月03日 (木)
Ajax [テクニカル]
Ajax (Asynchronus JavaScript + XML)
Google Maps, GMail, Google Suggestなどで用いられている技術。新しいものではなく、既存の技術の組み合わせ。Flash(Flex)などを使わずにリッチクライアントが作れる。
昔CSSとJavaScriptを少しいじっていた経験はあるので、目新しさはやはり感じないが、興味深い技術である。ちょっとしたサンプルならそれほど時間をかけずできそうだ。
JavaScriptの利用を強いるところは少しユーザビリティの問題を感じるが、リッチクライアントの需要はこれから高まっていくと思うので、Flashと並んで有力な技術の1つに位置づけられると思う。どちらかというとFlashはデザイナー主導、Ajaxは開発者主導の方法であるような印象を持った。
参考: Ajax: Web アプリケーション開発の新しいアプローチ
AjaxというかXMLHttpRequestを何か面白いことに使えないかな。。。と考える。
XMLではなくHTMLの断片を転送してそのままinnerHTMLに埋め込んでいる実装を見かけるがそれではあまり意味がないように思う。やはりデータ構造としてのXMLを転送することにしたい。もっというともとのコンテンツもHTMLである必要はなく、XMLでもかまわない。一昔前のXML+XSLTでドキュメント管理とあまり発想は変わらないが、そのときと違うことは、今はドキュメントをXMLで表現することが受け入れられてきているということである。RSSの流行などはその一例だろう。
投稿者 4bit : 21:48 | コメント (0) | トラックバック (0)