[coreserve][Amrita2] Amrita2化

Rubyカンファレンスのネタにする為に、できあがった画面をひとつAmrita2化してみました。

やったのは、こちらの一覧表示画面。この画面は、ゲスト/クライアント/カウンセラーで、それぞれ少しづつ違う画面になるので、ビューは次のような感じで、case文がたくさん含まれています。

<h1>カウンセリング予約システム</h1>
<br />
<p>
<%= "#{current_user.lastname} さん " if current_user %>
カウンセリング予約システムへようこそ
</p>
<ul>
<%- case current_user -%>
<%- when nil -%>
  <li><%= link_to 'ログイン', :controller => 'client', :action => 'login' %></li>
  <li><%= link_to '新規ユーザ登録', :controller => 'client', :action => 'signup' %></li>
<%- when Client -%>
  <li><%= link_to 'ログアウト', :controller => 'client', :action => 'logout' %></li>
  <li><%= link_to 'ユーザ情報変更', :controller => 'client', :action  => 'edit' %></li>
<%- when Counselor -%>
  <li><%= link_to 'ログアウト', :controller => 'client', :action => 'logout' %></li>
  <li><%= link_to 'ユーザ情報変更', :controller => 'client', :action  => 'edit' %></li>
  <li><%= link_to '管理', :controller => 'reservation', :action => 'index' %></li>
<%- end -%>
</ul>

Amrita2バージョンのテンプレートのこれに対応する部分は、

<h1>カウンセリング予約システム</h1>
<br />
<p>
<span id='user'> <span id='lastname' /> さん </span>
カウンセリング予約システムへようこそ
</p>
<ul>
  <li id='navi_links'><a id='link' amrita:type='link' /></li>
</ul>

と、かなりスッキリしています。ロジックはコントローラに移っているわけです。そこを抜き出すと

    @user = current_user
    case current_user
    when nil
      @navi_links = [
        { :link => [
            url_for( :controller => 'client', :action =>'login'),
            'ログイン'
          ]
        },
        { :link => [
            url_for( :controller => 'client', :action =>'signup'),
            '新規ユーザ登録'
          ]
        }
      ]
    when Counselor
      @navi_links = [
        { :link => [
            url_for( :controller => 'client', :action =>'logout'),
            'ログアウト'
          ],
      ....

一覧の所は、最後にリンクの表示が、ユーザ種別とセッションの状態の二重の分岐になっているのでかなりややこしいですが

<%- for counseling_session in @counseling_sessions -%>
  <tr>
    <td><%=h counseling_session.counselor_name %></td>
    <td><%=h counseling_session.status_j %></td>
    <td><%=h human_attribute_value(counseling_session, 'start') %></td>
    <td><%=h human_attribute_value(counseling_session, 'end') %></td>
    <td><%=h human_attribute_value(counseling_session, 'place') %></td>
    <td><%=h human_attribute_value(counseling_session, 'memo') %></td>
<%- case counseling_session.status -%>
<%- when :free -%>
  <%- case current_user -%>
  <%- when nil,Client -%>
    <td><%= link_to '予約', :action => 'reserve', :id => counseling_session %></td>
  <%- else -%>
    <td />
  <%- end -%>
<%- when :reserved -%>
  <%- if current_user == counseling_session.client -%>
    <td><%= link_to 'キャンセル', :action => 'cancel', :id => counseling_session %></td>
  <%- else -%>
    <td />
  <%- end -%>
<%- else -%>
    <td />
<%- end -%>
  </tr>
<%- end -%>

これも、Amrita2では、次のようにスッキリ。

    <tr id='counseling_sessions'>
      <td id='counselor_name' />
      <td id='status' />
      <td id='start' />
      <td id='end' />
      <td id='place' />
      <td id='memo' />
      <td id='navi'>
         <a id='navi_link' amrita:type='link' />
      </td>
    </tr>

もちろん、対応するロジックはコントローラの中にあるわけですが。

    @counseling_sessions = sessions.collect do |s|
      {
        :counselor_name => s.counselor_name,
        :status => s.status_j,
        :start => human_attribute_value(s, 'start'),
        :end => human_attribute_value(s, 'end'),
        :memo => human_attribute_value(s, 'memo'),
        :place => human_attribute_value(s, 'place'),
        :navi => {
          :navi_link =>
            case s.status
            when :free
              case current_user
              when nil,Client
                [ url_for(:action => 'reserve', :id => s), '予約']
              else
              end
            when :reserved
              if current_user == s.client
                [ url_for(:action => 'cancel', :id => s), 'キャンセル', ]
              end
            end
        }
      }
    end

この方式だと、動的な要素を抜き出して検証することが可能なので、テストがしやすいです。

  def test_index_for_client
    get :index, {}, :user => @cl1
    assert_response :success

    assert_equal ["http://test.host/amrita/cancel/10003", "キャンセル"],
                 assigns(:counseling_sessions)[0][:navi][:navi_link]
    assert_equal ["http://test.host/amrita/reserve/10002", "予約"],
                 assigns(:counseling_sessions)[1][:navi][:navi_link]
    assert_equal ["http://test.host/amrita/reserve/10001", "予約"],
                 assigns(:counseling_sessions)[2][:navi][:navi_link]
  end

それから、ERbの埋め込みは、やはりRailsと相性がいいと思います。jascaffoldで生成したテンプレートには、次のようにHelperメソッドを利用したページ遷移のリンクがあります。

<%= pagination_links @counseling_session_pages %>

ここは、次のように、Amrita2テンプレートの中にERbテンプレートを埋め込んで対応しました。

<span id='page_navi'><![CDATA[
<%= pagination_links $_[:pages] %>
]]></span>

コントローラのロジックはこのとおり。

    session_pages, sessions = paginate :counseling_sessions, list_param
    .....
    @page_navi = {
      :pages => session_pages
    }

こういうAmrita2向けのデータ(プレゼンテーションオブジェクト)の設定は、コントローラでなく、Helper モジュールの中に書いた方がいいかもしれません。

Railsのビュー(ERbテンプレート)は、ビューロジックとプレゼンテーションが混在してしまうと思います。Amrita2を使うことで、このように、ビューロジックとプレゼンテーションを分離して、ビューロジックだけをテストすることが可能になります。