gae+jruby+sinatra+haml+DataMapperでアプリケーション
[gae/jruby] gae+jruby+sinatra+haml+DataMapperでアプリケーションを作ってみる
何がしたいの?
- Google App Engine上でrubyアプリケーションを作成します。
どのように機能するの?
どこで確認したの?
- MacOSX (10.6.3 Snow Leopard) (Darwin Kernel Version 10.3.0)
- java 1.6.0_17
- ruby 1.8.7 (2009-06-08 patchlevel 173)
- google-appengine (google-appengine-0.0.11)
- sinatra (1.0)
いつ確認したの?
- 2010年4月11日
何が参考になるの?
gihyo.jp => 第9回 SinatraとSequel・Hamlで掲示板アプリを作る
code.google.com => Using the Datastore
d.hatena.ne.jp => Heroku+Sinatra+Haml+DataMapperで簡単自分用ブックマークサイト
どんなものができたの?
appspot.com => nnz-弁当店
お弁当を注文すると、24時間以内の注文数を表示するアプリケーションです。
お弁当は3つのメニューから選択可能で、注文した履歴が掲示板形式で表示されます。
何を参考にして作ったの?
gihyo.jp => 第9回 SinatraとSequel・Hamlで掲示板アプリを作るの記事を参考にしてサンプル的なお弁当注文サイトを作ってみました。
もちろん注文してもお弁当は届きませんのでご安心ください。
記事の順序に従ってGAEの掟に合わせた改造を施しながら進めていきます。元記事の筆者の方にはコードの部分転用の承諾をいただきました。
記事の中で「Sequel」を使用している部分は「DataMpper」で書き換えていきます。
リスト1 DataMpperによるモデル定義の例(model/order.rb)
require 'dm-core' DataMapper.setup(:default, "appengine://auto") class Orders include DataMapper::Resource property :id, Serial property :name, Text property :product, Text property :date, Time def date_jst (self.date+9*3600).strftime("%Y/%m/%d(%a) %H:%M:%S") end def count datastore = com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService() query = com.google.appengine.api.datastore.Query.new("Orders") datastore.prepare(query).countEntities() end end
上記のメソッドdate_jstは表記上の時刻を日本時間に変換するために9時間加算しています。
お弁当の購入時の現在時刻をデータとして保存すると、UTMの現在時刻が保存されます。それをローカルタイムに変換しようにもjrubyが稼働しているマシンもUTMがローカルタイムですので一筋縄では行きません。
やっつけ仕事的ですが9時間の加算を行い、表示するときだけ日本時間に無理やり変換しています。
またメソッドcountですが、gem "dm-aggregates"を利用すればDataMapperのCountメソッドが使用できるハズなのですが、力及ばずでLowレベルのAPIを使用しました。
リスト2 HamlによるHTML表現の例(views/layout.haml)
!!! XML !!! Strict %html %head %title nnz-弁当店 %meta{:"http-equiv"=>"Content-Type", :content=>"text/html", :charset=>"utf-8"} %link{:rel=>"stylesheet", :type=>"text/css", :href=>"/style.css"} %body %table{:width => "100%"} .banner %h1 nnz-弁当店ご注文β != yield .footer #column-a .column %ul %h3 nnz %li %a{:href => "/"} home %li %a{:href => "/"} products %li %a{:href => "/"} orders %li %a{:href => "/"} contact us
リンク先はダミーですが、なんちゃってフッターもつけてみました。
リスト3 HamlによるHTML表現の例(views/index.haml)
%form{:method => "POST", :action => '/order'} %input{:type => "hidden", :name => "_method", :value => "PUT"} %table %tr %fieldset %h3 お弁当何にする? .radio_group.toggle %p %input.radio{:type=>"radio", :name=>"product", :id=>"product_1", :value=>"のり弁", :checked=>true}/ %label.radio{:for=>"product_1"} のり弁 %p %input.radio{:type=>"radio", :name=>"product", :id=>"product_2", :value=>"唐揚げ弁当", }/ %label.radio{:for=>"product_2"} 唐揚げ弁当 %p %input.radio{:type=>"radio", :name=>"product", :id=>"product_3", :value=>"特盛りカレー", }/ %label.radio{:for=>"product_3"} 特盛りカレー %td 名前 %td %input{:type => "text", :name => "name"} %td %input{:type => "submit"} .today_order %h3 本日(24時間以内)のご注文は #{h @orders.count} 件です。 - @orders.each do |order| .comment %h2= h order.product .info %span.name== by #{h order.name} %span.date== (#{h order.date_jst})
掲示板を一般公開すると情報セキュリティ的にいろいろと心配ですので、ラジオボタンと名前欄だけのアプリケーションにしました。
自由入力が可能な名前欄はescape_html(hで別名定義済)でエスケープを行うこともセキュリティ上必要です。
リスト4 SassによるCSS表現の例(views/style.sass)
.banner margin: 0px 0% padding: 1em text-align: center color: #FFF background-color: #5998CF background: -webkit-gradient(linear, left top, left bottom, from(#70AEE5), to(#4080B6)) background: -moz-linear-gradient(top, #70AEE5, #4080B6) body margin: 0px 0% padding: 0px focus: autoline none h1 margin: 1em form margin: 0px 20% padding: 0.5em .today_order margin: 10px 20% padding: 0.5em .comment border: 1px solid black margin: 10px 20% padding: 0.5em h2 margin: 0px font-size: medium float: left .info span.name padding-left: 0.5em font-size: small span.date font-size: small .message margin-top: 1em .footer background: #EEE width: 100% font-size: 15px margin: 0px padding-bottom: 20px text-align: left !important .column width: 120px padding: 10px 0 5px 20px h3 font-size: 12px color: #444 font-weight: bold li font-size: 11px margin: 0 padding: 0 0 0 10px a color: #444 text-decoration: none padding: 0 10px margin-left: -10px ul margin-bottom: 20px list-style: none outside
CSSの代わりにSASSでスタイル指定を行います。
SASSを使えばタグ地獄から開放されるかな?思いきやイヤイヤこれも大変です。
メンテが楽になるのはおそらく間違いないと思いますが、新規作成には結構ひと苦労です。
リスト5 Sinatraによるコントローラ部分(start.rb)
require 'sinatra' require 'model/order.rb' helpers do include Rack::Utils alias_method :h, :escape_html end get '/style.css' do content_type 'text/css', :charset => 'utf-8' sass :style end get '/' do #24時間以内の注文 @orders = Orders.all(:date.gt => Time.now-24*3600, :order => [:date.desc], :limit => 100) haml :index end put '/order' do Orders.create({ :name => request[:name], :product => request[:product], :date => Time.now, }) redirect '/' end
Orders.allの部分で発注時刻が24時間以内の最大100レコードを時刻降順で取り出しています。
発注時刻は"Time.now"部分で画面が呼ばれた時刻が記録されていますが、これはGAEが動作している環境に依存する時刻です。もちろん日本時間ではありません。
リスト6 (Gemfile)
# Critical default settings: disable_system_gems disable_rubygems bundle_path ".gems/bundler_gems" # List gems to bundle here: gem "appengine-rack" gem 'dm-appengine' gem 'sinatra' gem 'haml'
'dm-appengine'部分がDataMapperを含んでいます。使用するgem名をGemfileへ記述しておくと、ダウンロードからjarファイルへの取り込みまで全部面倒を見てくれます。