南極の図書館

ペンギンが寝ていた…。

RailsによるアジャイルWebアプリケーション開発 03

P79からP106まで。
徐々に内容が濃くなってきた。30ページ弱とは思えない。

流れ

顧客が買い物できるようにするために、カタログとカートを作成する。
そこまでを合わせて。


タスクB:カタログの表示
顧客との対話を扱うコントローラ"store"の作成。
見栄えが悪いのでレイアウトを追加する。
価格を通貨としてフォーマットする。viewに埋め込みたくないのでヘルパーメソッドで。
カートに入れるボタンが無いので作成する。


タスクC:カートの作成
セッションの説明。Railsではcookieを使う。
セッションによる状態の保存を行う。
非データベースモデルの作成と統合を行う。
エラーの対処。フラッシュとloggerについて。
コントローラの重複を除去する。

タスクB:カタログの作成

ruby script/generate controller store index
まずはコントローラを作成する。indexはメソッド。


編集するのは、
app/controllers/store_controller.rb
app/models/product.rb
app/view/store/index.html.erb
app/view/layouts/store.html.erb
public/stylesheets/depot.css
あたり。普通に読んでいって特に難しいところは無し。


タスクC:カートの作成

これまでより一段階、難しいと思う。

カートについて

カートはビジネスロジックが含まれるのでモデルである。セッションを使うのためDBは必要ではない。(今回は)
背後にデータベースのないモデルを作るときは、ジェネレータを使わない。

作成手順

モデルにcart.rbを作成する。
store/index.html.erbのbutton_toにactionとidを追加する。
(このidでモデルオブジェクトとデータベースを識別)
store_controllerに、add_to_cartの動作を記述し、viewにもadd_to_cart.html.rbを作成する。

その後のイテレーション

要件:「わかってないわね。…(同じ商品が2つあったとき)商品は1行に表示して個数を2にするの」
→モデルcart_item.rbを作成し商品と数量の参照など、必要なものを書く。
attr_reader :product, :quantityなど。
なお、セッションの構成を変更した場合は、 rake db:sessions:clear を使ってセッションを削除しなければならない。


要件:堅牢性を高める。
存在しないURLを入力すると、エラーが起こることをなんとか処置しなければならない。
対応としては「例外を補足して無死する」こともできるが、アプリが反応しなくなるのはよくない。
そのため、以下の3つの処置を行う。
「内部でログを保存する」「無効な商品です。と出力する」「カタログページに戻る」


→フラッシュとloggerを使う。
商品が見つからない場合、ActiveRecordがRecordNotFound例外を発生させる。(SQLite3の場合)
インスタンス変数ではリダイレクトの時点で消滅するのでフラッシュを使うことになる。フラッシュはセッションに格納される。
loggerはどのコントローラももっている属性。機能としては他の言語とほぼ同じだと思われる。


要件:カートを空にできるように。
→viewとcontrollerにempty_cartを追加する。


要件:共通のコードをメソッドに抽出する。最後にカートの見栄えを整える。
→本の通りなので省略。


Rails関連

レイアウトとは。P82

テンプレートである。app/views/layoutsにコントローラと対応させて作成する。store.html.erbなど。
erbのheadに<%= stylesheet_link_tag "depot", :media => "all" %>と書くとタグを生成しdepot.cssにリンクされる。

params[:id]とは。P94

product = Product.find(params[:id])
アクションで使う使うオブジェクトのid(つまり主キー)が保持される。
button_to呼び出しに:id =>productを与えると動作する。

セッションとは。P89

コントローラ内にsessionというハッシュに似た特別なコレクションを保持する。
セッション情報は必ずアプリケーションの外部に格納する。(P90)
なお、セッションは以下2つのタスクでテーブルを作成し、
rake db:sessions:create
rake db:migrate
次にenvironment.rbでコメントアウトされている行を有効にすると動く。
Rails2.3以降では、config/initializers/session_store.rbの以下の行が該当する。
#ActionController::Base.session_store = :active_record_store
最後にstore_controllerにfind_cartメソッドをprivateで実装する。最終的には1行で書ける。
session[:cart] ||= Cart.new


※次の説明は、一度作成したあとに書かれている。P98

「アプリケーションレベルのオブジェクトをセッションデータに格納するのは、一般には本当に最悪」です。
CartクラスをActiveRecordオブジェクトにして、カートのデータをDBに格納すべき。(本書の例では、この説明のために敢えてそうしていない。)
セッションにはidを格納し、リクエストを受けたらidをセッションから抽出して、DBからカートを読む。
使用したヘルパーメソッド

image_tag(product.image_url):引数を画像のソースとし、HTMLの<img>タグを生成する。
number_to_currency(product.price):引数を通貨にフォーマットする。


button_to "カートに入れる":
 <div>を含んだ<form>を作成する。ここではカートに入れるボタンの追加。
 link_toはa hrefの生成なので不向き。button_toはPOSTリクエスト、link_toはGETリクエスト。
 以下のようなhtmlが生成される。

<form method="post" action="/store" class="button-to">
  <div>
    <input type="submit" value="カートに入れる" />
    <input name="authenticity_token" type="hidden" value="(略)=" />
  </div>
</form>
h(string)メソッド。

Railsでは "<%=h" とするとhtmlが展開されないので、慣習として最初はそうするべき。
例えば、image_tagを使用したときに、hメソッドを使うとこのように生成される。

&lt;img alt=&quot;Auto&quot; src=&quot;/images/auto.jpg?1265130093&quot; /&gt;

なお、erbのERB::Utilモジュールで定義されているとこちらに記載がありました。
http://www.metareal.org/2007/06/30/htmlescape-in-erb-util/

その他

:id =>product は :id =>product.id の簡略系。


find(:all, oder=> "title")
all(:order =>"title")
の2つは同じ。Rails2.3のscaffoldでは後者が生成される。


<%= @page_title || "Pragmatic Bookshelf" %>
インスタンス変数内の値をページの見出しに設定。


<%= yield :layout %>
yieldに:layoutを渡して呼び出す。Railsが自動でページ固有のコンテンツで置き換える。viewによって生成される。
yieldでなく@content_for_layoutでも良い。その方が読みやすいが、yieldの方が一般にかっこいいと言われているようだ。

最後に

いまいち理解できてない箇所。
params[:id]
それ以外は問題無いと思うので、このまま進む。