デイブトーマス本のサンプルをAmrita2化してみた
Agile Web Development With Rails: A Pragmatic Guide (The Facets Of Ruby Series)
これのチュートリアル部分で使われている depot というアプリを、開発中のAmrita2最新版で書いてみました。
storeコントローラーのViewだけ rhtml版と対比して示します。View以外はほぼ無修正で、a2html版(Amrita2で作成したビュー)とrhtml版は同じ結果を表示します。
それ以外のソースは、こちらのサポートページにあります。
(index.rhtml)
<h1>Your Pragmatic Catalog</h1> <% for product in @products -%> <div class="entry"> <%= image_tag(product.image_url) %> <h3><%= h(product.title) %></h3> <%= product.description %> <div class="price-line"> <span class="price"><%= number_to_currency(product.price) %></span> <% form_remote_tag :url => { :action => :add_to_cart, :id => product } do %> <%= submit_tag "Add to Cart" %> <% end %> </div> </div> <% end %>
(index.a2html)
<h1>Your Pragmatic Catalog</h1> <<div.entry :products < %= image_tag($_.image_url) <<h3:title>> <<:description>> <<div.price-line< <<span.price :price | :to_currency >> % form_remote_tag :url => { :action => :add_to_cart, :id => $_ } do %= submit_tag "Add to Cart" % end
(_cart.rhtml)
<div class="cart-title">Your Cart</div> <table> <%= render(:partial => "cart_item", :collection => cart.items) %> <tr class="total-line"> <td colspan="2">Total</td> <td class="total-cell"><%= number_to_currency(cart.total_price) %></td> </tr> </table> <%= button_to "Checkout", :action => :checkout %> <%= button_to "Empty cart", :action => :empty_cart %>
(_cart.a2html)
<<div.cart-title< Your Cart <<table:cart< %= render(:partial => "cart_item", :collection => $_.items) <<tr class="total-line"<------------------------------------- | colspan || 2 | | | class || | total-cell | | || Total | %= $_.total_price.to_currency | ----------------------------------------------------------- %= button_to "Checkout", :action => :checkout %= button_to "Empty cart", :action => :empty_cart
(_cart_item.rhtml)
<% if cart_item == @current_item %> <tr id="current_item"> <% else %> <tr> <% end %> <td><%= cart_item.quantity %> X </td> <td><%= h(cart_item.title) %></td> <td class="item-price"><%= number_to_currency(cart_item.price) %></td> </tr>
(_cart_item.a2html)
<< :cart_item { :current => ($_ == @current_item), :price => $_.price.to_currency } < <<tr id="current_item" : | Attr[:id=>:current] <------------------------------------------------ |class|| | | item-price | | ||<<:quantity>> X |<<:title>>| <<:price>> | -------------------------------------------------
ここで使っている to_currency というメソッドは、次のように定義してあります。
module PriceHelper include ActionView::Helpers::NumberHelper def to_currency number_to_currency(self) end end class BigDecimal include PriceHelper end class Integer include PriceHelper end
(checkout.rhtml)
<div class="depot-form"> <%= error_messages_for 'order' %> <fieldset> <legend>Please Enter Your Details</legend> <% form_for :order, :url => { :action => :save_order } do |form| %> <p> <label for="order_name">Name:</label> <%= form.text_field :name, :size => 40 %> </p> <p> <label for="order_address">Address:</label> <%= form.text_area :address, :rows => 3, :cols => 40 %> </p> <p> <label for="order_email">E-Mail:</label> <%= form.text_field :email, :size => 40 %> </p> <p> <label for="order_pay_type">Pay with:</label> <%= form.select :pay_type, Order::PAYMENT_TYPES, :prompt => "Select a payment method" %> </p> <%= submit_tag "Place Order", :class => "submit" %> <% end %> </fieldset> </div>
(checkout.a2html)
<%(BeforeCompile) use_macro(StandardForm) %> <div class="depot-form"> %= error_messages_for 'order' << standard_form record="order" < <form_title>Please Enter Your Details</form_title> <url action = "save_order" /> <<header< % pay_select = select "order", :pay_type, % Order::PAYMENT_TYPES, :prompt => "Select a payment method" <<<-------------------------------------------------------------- ||| Name || <text id="name" size="40" /> | <<<-------------------------------------------------------------- ||| Address || <text_area id="address" rows="3" cols="40" /> | <<<-------------------------------------------------------------- ||| E-Mail || <text id="email" size="40" /> | <<<-------------------------------------------------------------- ||| Pay with || <<field id="pay_type"< | ||| || <<:pay_select\|NoSanitize>> | --------------------------------------------------------------- <<fotter< %= submit_tag "Place Order", :class => "submit"
このビューでは、standarad-form というAmrita2のマクロを定義して、使用しています。
standard-formのソースは次のとおりです。
(helper/standard_form.rb)
class StandardForm < Amrita2::Macro::Base TemplateText = <<-END_OF_TEMPLATE <<fieldset< <<legend:title>> <%%= form_tag(<%= $_[:url].inspect %>) %%> <<:header>> <<p:items< <<label:label|Attr[:for]< <<:text>>: <<:input>> <<:fotter>> <%%= '</form>' %%> END_OF_TEMPLATE def macro_data(element) root = element.as_amrita_dictionary record = root[:record].intern title = element.search("form_title").first url = element.search("url").first header = element.search("header").first items = element.search("tr").collect do |tr| tr_to_data(record, tr) end fotter = element.search("fotter").first ret = { :title => title.contents, :record => record, :url => url ? url.as_amrita_dictionary : {}, :header => header.contents, :items => items, :fotter => fotter.contents } ret end def tr_to_data(record, tr) label = tr.search("th").first td = tr.search("td").first input = td_to_input(record, td) || { } { :label => { :for => "#{record}_#{input[:field_id]}", :text => label.contents.strip, }, :input => input[:input] } end def td_to_input(record, td) x = td.children.find { |c| c.kind_of?(Hpricot::Elem) } raise "input was not found in <td>" unless x attr = x.as_amrita_dictionary field_id = attr.delete(:id) input = case x.name when "text" "<%= text_field #{record.to_s.inspect}, #{field_id.to_s.inspect}, #{attr.inspect} %>" when "text_area" "<%= text_area #{record.to_s.inspect}, #{field_id.to_s.inspect}, #{attr.inspect} %>" when "field" x.contents else raise "unknown input field #{x.name} " end { :input => Amrita2::SanitizedString[input], :field_id => field_id } end end
(layout/store.rhtml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- ! Excerpted from "Agile Web Development with Rails, 2nd Ed." ! We make no guarantees that this code is fit for any purpose. ! Visit http://www.pragmaticprogrammer.com/titles/rails2 for more book information. --> <html> <head> <title>Pragprog Books Online Store</title> <%= stylesheet_link_tag "depot", :media => "all" %> <%= javascript_include_tag :defaults %> </head> <body id="store"> <div id="banner"> <%= image_tag("logo.png") %> <%= @page_title || "Pragmatic Bookshelf" %> </div> <div id="columns"> <div id="side"> <% hidden_div_if(@cart.items.empty?, :id => "cart") do %> <%= render(:partial => "cart", :object => @cart) %> <% end %> <a href="http://www....">Home</a><br /> <a href="http://www..../faq">Questions</a><br /> <a href="http://www..../news">News</a><br /> <a href="http://www..../contact">Contact</a><br /> </div> <div id="main"> <% if flash[:notice] -%> <div id="notice"><%= flash[:notice] %></div> <% end -%> <%= yield :layout %> </div> </div> </body> </html>
(layout/store.rhtml)
<%(BeforeCompile) opt[:process_xhtml]=true %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- ! Excerpted from "Agile Web Development with Rails, 2nd Ed." ! We make no guarantees that this code is fit for any purpose. ! Visit http://www.pragmaticprogrammer.com/titles/rails2 for more book information. --> <<html< <<head< <title>Pragprog Books Online Store</title> %= stylesheet_link_tag "depot", :media => "all" %= javascript_include_tag :defaults <<body#store< <<div#banner< %= image_tag("logo.png") %= @page_title || "Pragmatic Bookshelf" <<div#columns< <<div#side< % style = @cart.items.empty? ? "display: none" : false <<div#cart : | Attr[:style] < %= render(:partial => "cart", :object => @cart) <a href="http://www....">Home</a><br /> <a href="http://www..../faq">Questions</a><br /> <a href="http://www..../news">News</a><br /> <a href="http://www..../contact">Contact</a><br /> <<div#main< <<div#notice ?[flash[:notice]]< %= flash[:notice] << :content_for_layout | NoSanitize >>