3月にVoiceXMLとRuby on Railsという記事を書きましたが、Ruby on Rails 2.0.2 で作り直してみました。
# routes.rb に追加 -> http://localhost:3000/weather/state1.vxml map.connect ':controller/:action.:format'
# 001_create_region.rb class CreateRegions < ActiveRecord::Migration def self.up create_table :regions do |t| t.string :name t.string :yomi t.string :weather t.timestamps end end def self.down drop_table :regions end end # 002_add_test_data.rb class AddTestData < ActiveRecord::Migration def self.up Region.delete_all Region.create :name => '東京', :yomi => 'とうきょう', :weather => '晴れ' Region.create :name => '横浜', :yomi => 'よこはま', :weather => '曇り' Region.create :name => '大阪', :yomi => 'おおさか', :weather => '雨' Region.create :name => '京都', :yomi => 'きょうと', :weather => '晴れ時々曇り' Region.create :name => '広島', :yomi => 'ひろしま', :weather => '曇りのち雨' end def self.down Region.delete_all end end class Region < ActiveRecord::Base end
class WeatherController < ApplicationController def state1 # counter をセッションで記憶する if !session[:counter] session[:counter] = 1 else session[:counter] += 1 end @regions = Region.find(:all) end def state2 if params[:region] @region = params[:region] @weather = Region.find_by_name(@region).weather end end end
[state1.vxml.erb] <?xml version="1.0" encoding="UTF-8"?> <vxml version="2.0" xml:lang="ja"> <form id='ask'> <field name='region'> <prompt timeout='20s'> <%= session[:counter] %>番です。 天気を聞きたい地域を、 <% @regions.each do |item| -%> <%=h item.name %> 、 <% end -%> から選んでください。 </prompt> <grammar version='1.0' root='#region_rule'> <rule id='region_rule'> <one-of> <% @regions.each do |item| %> <item> <token sym="<%=h item.yomi %>" slot='region'> <%=h item.name %> </token> </item> <% end %> <item> <token sym='まいくてすと'>マイクテスト</token> </item> </one-of> </rule> </grammar> </field> <block> <submit next='<%= url_for(:action=>"state2") %>.vxml'/> </block> </form> </vxml> [state2.vxml.erb] <?xml version="1.0" encoding="UTF-8"?> <vxml version="2.0" xml:lang="ja"> <form id='answer'> <block id='answer'> <log>地域=<%=h @region %>:天気=<%=h @weather %></log> <prompt> <%=h @region %>の天気は<%=h @weather %>です。<break /> </prompt> </block> <block> <goto next='<%= url_for(:action=>"state1") %>.vxml'/> </block> </form> </vxml>
以前は rxml で VoiceXML を生成してみたのですが、やはり vxml.erb の方がすっきりしました。
そして興味深いことに、同じコントローラーで html.erb も動かすことができそうです。
[state1.html.erb] <html> <body> <p> <%= session[:counter] %>番です。 </p> <% form_tag :action => 'state2' do %> 天気を聞きたい地域: <select name="region"> <% @regions.each do |item| -%> <option value="<%=h item.name %>"><%=h item.name %></option> <% end -%> </select> <%= submit_tag "実行" -%> <% end -%> </body> </html> [state2.html.erb] <html> <body> <p> <%=h @region %>の天気は<%=h @weather %>です。</p> <%= button_to '戻る', :action => 'state1' %> </block> </body> </html>
これまでISTCのマルチモーダル対話記述ワーキンググループで提案してきたアーキテクチャ階層:
- 西本 卓也: “マルチモーダル対話システムのためのアーキテクチャ階層化,” FIT2006イベント企画「音声・マルチモーダル対話記述とその標準化」予稿集, Sep 2006. (PDF)
と対応させてみると、
- 6層「データモデル」が ActiveRecord
- 5層「タスク間制御」が Controller のアクション
- 4層「タスク内制御」以下が View (html.erb または vxml.erb)
になっており、モダリティごとの View を統一的に記述する方法を見つけることが、4層と3層を分離して記述するためのステップになりそうです。ヘルパーメソッドやパーシャルなどの機能がその部品になるでしょう。また、コントローラーのモダリティ独立性という観点からも RESTful であることが重要だ思います。