Waves チュートリアル パート3
http://www.rubywaves.com/tutorial-3
パート1の翻訳はこちら。
パート2の翻訳はこちら。
-
-
-
- -
-
-
先に進む前に,今までやってきたことの簡単なおさらいです。
- waves コマンドで新規プロジェクトを作った
- データベース設定ファイル configurations/default.rb を自分の環境用に修正した
- rake schema:migration と rake schema:migrate を使って,最初のスキーマを作成した
- waves コンソールを使い,Waves が正しくデータベースからモデルを作ったかを確認して,テスト用のレコードを追加した
- レイアウトと再利用ビューを使い,ブログエントリの一覧,表示,編集テンプレートを作った
- レコードの追加,更新を行うのに,REST スタイルのインターフェースを用いた.同じ URL が GET か POST かによって異なったマッピングがなされる.
ビューのコードを書くだけでこれら全てのことができました。テンプレートにアクセスするとき,データがなんらかの方法でデータベースから取り出されて,インスタンス変数に詰め込まれたのは明らかです。
モデルにコメントを追加することで,もう少し複雑なことをしてみます。こうするには,モデルを明示的に定義しなければならず,Waves のデフォルトをどのようにオーバライドするかが明らかになります。
コメント用に新しいマイグレーションを追加します。
~/blog $ rake schema:migration name=add_comments
生成されたマイグレーションファイルを編集します(scheme/migrations ディレクトリにあって,002_add_comments.rb のような名前になっていることを思い出してください)。編集が終われば,こんな風になっているはずです。
class AddComments < Sequel::Migration def up create_table :comments do primary_key :id foreign_key :entry_id, :table => :entries text :name text :email text :content timestamp :created_on end end def down drop_table :comments end end
timestamp を追加したことに注意してください。これは Sequel によって自動的に設定されます。
マイグレーションを実行します。
~/blog $ rake schema:migrate
次に Entry モデルを,コメントを保持するように修正します。ここが面白いところです。なぜなら Entry モデルなんて作っていませんから。でも問題ありません。Waves では,何かを明示的に定義すれば,それはデフォルトをオーバライドするようになっています。なので,単純に entry.rb を models ディレクトリに作って修正します。一番簡単なのはジェネレータタスクを使うことです。
~/blog $ rake generate:model name=entry
では entry.rb を開き,編集してください。コメントの関連を追加する必要があります。こんな感じです。
module Blog module Models class Entry < Sequel::Model before_save do set(:updated_on => Time.now) if columns.include? :updated_on end one_to_many :comments, :from => Blog::Models::Comment, :key => :entry_id end end end
before_save 部は,ジェネレータタスクが自動的に作り,これは default.rb のデフォルト実装に基づいています。comments への一対多関連を,Sequel の one_to_many マクロを使って追加しました。これは Rails の has_many と同じですが,ちょっとだけ分かりやすい名前になってます。
次に同じことを Comments に行い,コメントが Entry を参照するようにします。まず,さきほどと同じように,モデルを生成します。
~/blog $ rake generate:model name=comment
次に関連を追加します。こんな感じです。
module Blog module Models class Comment < Sequel::Model(:comments) before_save do set(:updated_on => Time.now) if columns.include? :updated_on end one_to_one :entry, :from => Blog::Models::Entry end end end
では entry/show テンプレートにコメントの一覧表示と追加機能を付け加えましょう。こんな風になるはずです。
layout :default, :title => @entry.title do a 'Show All Entries', :href => '/entries' h1 @entry.title textile @entry.content h1 'Comments' view :comment, :add, :entry => @entry view :comment, :list, :comments => @entry.comments end
ここでやってることはコメント関連のビューをフォームに埋め込んでいるだけです。したがって次にやるべきことは,そのビューを作ることになります。コメント追加用のビューから始めましょう。templates/comment に add.mab を作ります。
form :action => "/comments", :method => 'POST' do input :type => :hidden, :name => 'comment.entry_id', :value => @entry.id label 'Name'; br input :type => :text, :name => 'comment.name'; br label 'Email'; br input :type => :text, :name => 'comment.email'; br label 'Comment'; br textarea :name => 'comment.content', :rows => 10, :cols => 80; br input :type => :submit, :value => 'Save' end
簡単ですね。REST スタイルの決まり事を使い,新しいオブジェクトを POST メソッドで /comments に追加します。hidden フィールドに entry_id を持つので,どのエントリにコメントを追加すればいいのか分かります。コメントはエントリ画面でのみ編集されるので,レイアウトは不要です。
次にコメント一覧です(templates/comment/list.mab)。
@comments.map{ |c| c }.sort_by( &:created_on ).each do |comment| p 'Posted on ' << comment.created_on.strftime('%b %d, %Y') << ' by ' << ( ( comment.name.nil? or comment.name.empty? ) ? 'anonymous coward' : comment.name ) textile comment.content end
(Sequel の MySQL アダプタの制限のため,不格好な map が必要になります)
これでそれぞれのエントリーに,追加用フォーム,コメント一覧ができました。
ブログのエントリにコメントを追加してみましょう。/entries にアクセスして,'My First Blog Entry' をクリック,コメントを追加します。
http://www.rubywaves.com/images/adding-a-comment?size=medium
"Save" をクリックすると…おっと。404 ですね(200 で空白ページの場合もあるようです)。何が起きたのでしょうか。問題はデフォルトの URL マッピング(Rails でいうルート)だと,指定のリソースを追加したあとにエディタを読み込もうとするためです。今回はエディタを定義していませんし,単に追加したコメントを表示したいだけです。なので,新しいマッピングルールを追加することにします。
これはとても分かりやすいので,Waves のマッピングルールを見るのに向いています。configurations ディレクトリの mapping.rb を開いてください。中を見てみましょう。
module Blog module Configurations module Mapping extend Waves::Mapping # your custom rules go here include Waves::Mapping::PrettyUrls::RestRules include Waves::Mapping::PrettyUrls::GetRules end end end
Waves の他の部分と同じように,まずアプリケーションモジュール(このケースでは Blog)をオープンしています。マッピングは設定の一部なので Configurations モジュールもオープンしています。最後に自前の Mapping モジュールを定義しています。これが Waves がリクエストを処理するときに参照する部分です。
マッピングモジュールは通常 Waves::Mapping を extend して,マッピングメソッドを使えるようにして,よく使われるパターンをまとめた定義済みのルール集を include します。自分用のパターンを定義することもできます。デフォルトでは,Waves は追加,更新,削除ルールの REST スタイルのキレイな URL を提供します。
今回のケースでは,コメント用に自前のルールが必要です。どうすれば新しいルールを追加できるのでしょう? やり方はいくつかありますが,ここでは path メソッドを使います。path メソッドは URL とマッチする正規表現,制約のハッシュ,そして1つのブロックを引数にとります。新しいコメントを追加するために,デフォルトをオーバーライドしましょう。"your custom rules go here" というコメントのすぐ下に追加します。
path %r{^/comments/?$}, :method => :post do use( :comment ); comment = controller { create } redirect( "/entry/#{comment.entry.name}" ) end
このルールは,comment をモデル,ビュー,コントローラとして使い,コントローラの create メソッドを呼び出し,comment の entry にリダイレクトするいう意味です。
ブラウザの「戻る」ボタンを押して,もう一度コメントを追加してみてください。コメントが2つ追加されているはずです。これは最初に "save" をクリックしたときにもコメントが保存されたのに,正しい URL にリダイレクトされてなかったからです。
チュートリアルの次のパートでは,スタイルシートと少しの JavaScript を追加して,Blog アプリを少し見栄えよくします。準備が出来たらパート4へ。