レヴュー: MasterView - Part 2 MasterViewの仕組み
Railsのテンプレートはレイアウト、コンテンツ、パーシャルに分類されます。ERBを使うとこれらは別のファイルになるのですが、なんとMasterViewでは一つのXHTMLファイルです。そしてそのファイルは、レンダリング結果のサンプルとしてブラウザで表示できます。さらに、そのファイルをWYSIWYGなHTMLエディタで編集しても、テンプレートとして壊れません。
MasterViewはどのようにしてそんな芸当を実現しているのでしょうか。MasterViewは独自のディレクティブをHTMLエレメントの属性として埋め込みます。例えば見出しを動的に生成するには次のように書きます。
<h1 mv:content="@title">Sample Title</h1>
上の見出しはそのままブラウザで表示すれば「Sample Title」ですが、レンダリングすると@title
の値が入るというわけです。
このようなディレクティブをXHTMLエレメントに加えることで、MasterViewはXHTMLファイルを複数のテンプレートに分割します。一般的には、最も外側の要素であるにレイアウトのテンプレート名を与えます。これは
<html mv:generate="layouts/foo.rhtml"> </html>
のように書きます(便宜上、他の属性は省いています)。このように書くと、<html></html>内の内容がlayouts/foo.rhtml
というテンプレートになります。
そして<html></html>の中に別のテンプレート名を指定された子孫要素があれば、その要素はレイアウトテンプレートから外され、新たなテンプレートとして認識されます(←ここがミソ)。
<html mv:generate="layouts/foo.rhtml"> <head><title>タイトル</title></head> <body> <div mv:replace="@content_for_layout"> <div mv:generate="foo/index.rhtml"> メインコンテンツ </div> </div> </body> </html>
(ちょっと端折りましたが、<div>mv:replace="@content_for_layout"</div>
は<%= @content_for_layout %>
になります。)上の内容をMasterViewでパースすると2つのテンプレートができるわけです。このパースは自動的に行われます。
パーシャルの切り出し方もほぼ同じです。ただしディレクティブにはmv:gen_partial
を使います。
<html mv:generate="layouts/foo.rhtml"> <head><title>タイトル</title></head> <body> <div mv:replace="@content_for_layout"> <div mv:generate="foo/index.rhtml"> メインコンテンツ <p mv:gen_partial=":partial => 'foo/message'"> パーシャル </p> </div> </div> </body> </html>
上のコードからはfoo/_message.rhtml
が生成されるというわけです。ここまでの内容はMasterViewのサイトで図で説明されています。
テンプレートの再利用
作ったテンプレートは再利用しなければ意味がありません。ERBではテンプレートの再利用はレンダリング時にのみ発生しますが、MasterViewではテンプレートの段階で再利用できる必要があります。例えば一つのXHTMLファイルでレイアウトを定義したら、そのレイアウトを利用する別のファイルをブラウザで表示した時、指定したレイアウトで表示される必要があります。
そのためにMasterViewには、指定したテンプレートを別のXHTMLファイルにコピーするrakeのタスクが用意されています。コピーするレイアウトはmv:import
で指定します。例えば上のレイアウトlayouts/foo.rhtml
をfoo/show.rhtml
で再利用するには次のように書きます。
<html mv:import="layouts/foo.rhtml"> <div mv:replace="@content_for_layout"> <div mv:generate="foo/show.rhtml"> showする </div> </div> </html>
ユーザは<head>や<title>や<body>を手作業でコピーする必要はありません。上のようにコピーしたいテンプレートの名前だけをmv:import
で指定して、rake mv:rebuild_allを実行すると、レイアウトがコピーされて下のようになります。
<html mv:import="layouts/foo.rhtml"> <head><title>タイトル</title></head> <body> <div mv:replace="@content_for_layout"> <div mv:generate="foo/show.rhtml"> showする </div> </div> </body> </html>
同様に、パーシャルを再利用するにはmv:import_render
を使います。
<html mv:import="layouts/foo.rhtml"> <head><title>タイトル</title></head> <body> <div mv:replace="@content_for_layout"> <div mv:generate="foo/show.rhtml"> showする <p mv:import_render=":partial => 'foo/message'"> </p> </div> </div> </body> </html>
以上がMasterViewのキモとなる、テンプレートに関するディレクティブです。
このようにレンダリング前からテンプレートを再利用可能にするMasterViewのrakeですが、実際には致命的な欠陥があります。次回はその点を含め、 MasterViewの欠点を遠慮なく挙げていきます。