RESTful におけるHTMLコンテンツ(!=ドキュメント)のURIとは。。。

ついついこういうことをしたくなります。

<!-- http://example.org/results -->
<html>
<body>
  <form id="query" method="post" action="result">
  Current Query : <input name="query" value="queryString"></input>
  </form>
  <h1>Results</h1>
  <ul>
    <li>result1</li>
    <li>result2</li>
    <li>result3</li>
  </ul>
</body>

ようするになんかの結果を表現するリソースが /results であり、post (本当はputがいい) するたびに新しくリソース/result が作られます。

普通のCGIならいいんだよ、これで。

ところが、このFormの部分をRailsAjax化しようとすると次のようになるはず。

<!-- http://example.org/results -->
<html>
<body>
  <% form_remote_tag :update => "result", :url => result_path() :method=>"put" do -%>
  Current Query : <input name="query" value="queryString"></inptu>
  <% end -%>
  <h1>Results</h1>
  <div id="result">
  <%= render :partial => "_result_list" %>
  </div>
</body>

これだと、http://example.org/results について、

  • 初回URIアクセス時はすべてのHTMLを返す
  • 2回目以降のURIアクセス時は_result_listだけを返す

ということが必要になることが容易に想像がつきます。そして、RESTを多少かじった身で、REST"ful"にやるのであれば、厳密には3つのリソースが必要な気がしてきます。

<!-- 結果を文書構造表現 -->
<html>
<body>
   <form id="query" src="/result;query" onSubmit="Ajax.Updater.new('result')" ></div>
   <h1>Results</h1>
   <div id="result_content" src="/result"></div>
</body>
</html>
<!-- 結果を文書構造表現を受け取るためにクライアントが使用する問い合わせ文書構造表現 -->
  Current Query : <input name="query" value="queryString"></input>
<!-- 結果のみを含む表現 -->
  <ul>
    <li>result1</li>
    <li>result2</li>
    <li>result3</li>
  </ul>

何が言いたいかというと、

  • /result にアクセスしたときはHTML全部を返す。
  • /result;dom_id にアクセスしたときはHTML内のdom_id要素以下を返す。

を妥当なオーバーヘッドの範囲でやってくれないかなぁ、ということです。妥当なオーバーヘッドの範囲、というのがくせ者で、おそらく毎回サーバー側でDOMを構成して、XPathをかける、というのはひどそう。これが真ならそもそものアイディアとして、form_remote_tag に取得したいXPATH式、じゃなくてDOM Element IDをかけるようにしたい。

とかいろいろ考えたんだけれども、結局落ち着いたのは以下。

_method ならぬ _layout パラメーターを用いてみる。

  • クライアント側。
  <% form_remote_tag :update => "result", :url => result_path(:_layout => "content" ) :method=>"put" do -%>
||

- サーバー側
>|ruby|
  layout :_layout
  protected
  def _layout
    if request.format == Mime::HTML
      case params[:_layout]
      when "content", "form"
        params[:_layout]
      else
        nil
      end
    end
  end
||

これで、?_layout= 指定がなければHTML全体、_layout="content" のときはdivタグのみ。とかの制御をlayoutを使う使わない、で何とか実現はできる。

こんな面倒なことを考えなければならない原因はなんだろう、と思ったら、HTMLは、URIが指し示すリソース以上の内容を持つことを想定して設計されていないか、という話で。