ビュー
出典: AgileKnowledge
grailsのビュー (ここでのビューとはMVCのV, Webプレゼンテーション層) の仕組みを見 てみます.
目次
1 grailsのビュー 1.1 grails tagとは?
1.2 JSP tagとどこが違うのか? 2 grails tagの定義
2.1 Dynamic Tag Library 2.2 artefactとしてのTagLib 2.3 Syntax Tag
3 GSPパーサ
3.1 GSP中のgrails tag 3.2 GSPの他の構文要素
grailsのビュー
grailsのビューはおもに, GSPとgrails tagを使って実現されています. GSP (groovy server pages) はその名前の通り, JSP (Java Server Pages) によく似た技術です. grails tagは同じようにJSPで使われているtaglibに相当します (grailsでもtaglibと呼ばれていま す. JSPのtaglibと区別するためにgrails tagと呼ぶことにします). grails tagはおおざっぱ に言えば, groovyコードを呼び出すHTML用のマクロですね.
grails tagとは?
例えばscaffoldに使われるテンプレート
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/grails/templates/scaffolding を見てみましょう (テンプレートのマクロとGSPのマクロが混在しているので分かりにく
いかもしれませんが...). ここにはscaffoldで生成されるWebページのテンプレート ({create,edit,list,show}.gsp) があります.
この中で<g:...> ... </g:...>がgrails tagを使っている部分です.
JSP tagとどこが違うのか?
grails tagとJSP tagがどこがどう違うのでしょう. grails.org―Tag+Library+Reference (http://grails.org/Tag+Library+Reference) によれば, 例えば次のような違いがありま す.
パラメータの${...}の中はgroovyコード (JSPではJSTL式言語) カスタム・タグを書くのが簡単 (JSP tagは手間が多くて, 非直感的) GStringが使える (JSP tagでは<c:out value=... />)
上原さんの日記d.hatena.ne.jp―20071226 (http://d.hatena.ne.jp/uehaj/20071226) も参考になるでしょう.
要は, JSP tagに較べてgrails tagは「読みやすく, 書きやすい」と言っていいと思います. ただし基本的な考え方は似ており, JSPに較べて画期的なビュー技術だとまでは言えない, ということがこのページを読めば分かるでしょう.
grails tagの定義
grails tagは以下の場所で実装されています. org.codehaus.groovy.grails.web.taglib
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/taglib 1.
org.codehaus.groovy.grails.plugins.web.taglib
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/groovy/org/codehaus/groovy/grails/plugins/web/taglib 2.
org.codehaus.groovy.grails.web.taglib.jsp
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/taglib/jsp 3.
またユーザが定義したカスタム・タグXXXは, XXXTagLib.groovyとして<grails-app> /grails-app/taglibに置きます.
Dynamic Tag Library
まずはユーザがgroovyで書いて追加する, カスタム・タグ (カスタム・タグの集まりは dynamic tag libraryと呼ばれる) から見てみることにしましょう (カスタム・タグの書き 方はGrails - Dynamic Tag Libraries (http://grails.org/Dynamic+Tag+Libraries) にあ ります).
カスタム・タグと同じ構造を持つ, 組込みのgrails tagはgrails tagの定義(2)の場所で定義 されています. 例えばFormatTagLibを見てみましょう.
!!"#$%&'!(#)$*)+*$"+&%,!-..
!!!+/!0001"#$%&'+$221*$"&%,13..4$"5%,0"#..67
要素を持たないタグは一引数のクロージャとして表されています. 要素を持たないタグとは
のように使われるタグです. 例えば次のように定義します.
クロージャ名がタグ名を表しています. 唯一の引数, attributesはタグ属性の詰まったMap です.
クロージャ内に上記のようなコードがあります. ここでは...の部分が「そのまま」(評価さ れて) GSP中のタグ部分に置き換わります.
さて, ここで (grailsに付きものの) 謎がひとつあります. outはどこから来たのでしょう? その他にも見慣れない何かが漂っていますね:-) それについては後で見ることにしましょう. 一方, 要素を持つタグは二引数のクロージャとして表されています. 要素を持つタグとは
のように使われるタグです. 例えば次のように定義します.
ここで, bodyは要素を処理するクロージャです (今の例だったら<baz>...</baz>の部分を処 理してくれます). body()として呼び出せば, 要素を評価した文字列が返されることになり ます. またbodyに渡された引数はGSP上のタグ中では${it}として参照できます.
タグを定義するクロージャ (例えば上記のfoo) はそのままGSP上のタグ中でメソッドとし て, "${foo()}"のように呼び出すこともできます.
このように, GSP上でのDynamic Tag Libraryは, groovyのクロージャを活かした, 非常 に単純かつ強力なメカニズムです.
artefactとしてのTagLib
上のようにして定義するTagLibはartefactの一種です.
artefactとは何だったでしょう? 前のpluginのときの話を思い出してみて下さい. artefact
8"9-..!2$#$:;<=,$#=!2$#$:><=,$?=!1/
@)-!-..!<!A!$**#%,B*)'!+/!000!C
.B*!88!=000=
8"9-..!2$#$:;<=,$#=/
!!!!!!!!8,$?/!000!81,$?/ 81"9-../
@)-!-..!<!A!$**#%,B*)'D!,.@7!+/!000!C
とはgrailsが作って (create-xxx) 管理する特別な種類のオブジェクトのことです. domain class, controller, serviceなどみんなartefactの例です.
artefactとしてのTagLibは以下の場所で定義されています. org.codehaus.groovy.grails.commons
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/commons/org/codehaus/groovy/grails/commons TagLibArtefactHandler
GrailsTagLibClass DefaultGrailsTagLibClass
XXXTagLibクラス中に, 上で述べたoutのようなものを実際にinjectしているのは ControllersGrailsPlugin
(http://fisheye.codehaus.org/browse/grails/trunk/grails/src/groovy/org/codehaus/groovy/grails/plugins/web/ControllersGrailsPlugin.groovy
#doWithDynamicMethodsです. 今のところtaglibはcontrollerと切り離せないみたいで すね.
Syntax Tag
Javaで書かれた組込みのタグも見てみることにしましょう. これらはdef, if, whileなどお もに構文的な面を定義しているタグなのでまとめてSyntax Tagとも呼ばれています. Syntax Tagはgrails tagの定義(1)の場所で定義されています. Syntax Tagは, ひとつのタ グごとにGrailsTagインタフェースをimplementsしたJavaクラスで書かれています. GrailsTagインタフェースで定義されているメソッドは以下の通りです. つまり, 各タグは 以下のメソッドを定義しなければなりません. でも各メソッドの意味はほとんど自明です ね.
6.%@!%E%*FG$2!*$"H.E*)I*J 6.%@!')*K#%*)#FK#%*)#!LJ
6.%@!')*M**#%,B*)'FG$2!$**#%,B*)'J
6.%@!')*M**#%,B*)FN*#%E"!E$:)D!O,P)(*!6$&B)J 6.%@[email protected]*$#*4$"FJ
6.%@[email protected]@4$"FJ N*#%E"!")*R$:)FJ
例えば<g:if>を定義しているクラスを見てみましょう (他のSyntax Tagである, (.&&)(*,
@)-, )$(S, LS%&), %-1)&')1)&')%-, -%E@M&&, "#)2もみな同じです).
これに対応するタグはGSP中では以下のように使われます.
(&$''!T#..67U-4$"!)I*)E@'!T#..67N7E*$I4$"!A!000!C
これが以下のように展開されるはずです (GSP中にgroovyコードとして埋め込まれる).
ちなみにここにある他のクラスは以下のようなものです.
GroovyInputTag extends RequestContextTag (renderInput) これは今は使われていない (2006年秋 deprecated) GroovyPageTagBody
XXXTagLibで使われるbodyクロージャを表すクラス GrailsTagRegistry
定義されているタグの格納庫
GSPパーサ
さて, タグはおもにDynamic TagとSyntax Tagがあり, それぞれどのようにして定義され ているか, どのように定義すればいいかが分かったとして, ではいったい誰がこれらのクラ スを呼んでいるのでしょう?
それがGSPパーサ (org.codehaus.groovy.grails.web.pages.Parse
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/Parse.java ) です. 少し見てみましょう.
ParseはTokensインタフェースをimplementしています. Tokensインタフェースでは以 下のようなトークンを定義しているだけです. つまりGSPはこういう構文要素を持ってい ると考えてよさそうです.
'*$*%(!-%E$&!%E*!QO3!<!+;V '*$*%(!-%E$&!%E*!W4G5!<!XV
'*$*%(!-%E$&!%E*!YQZ[\!<!;V!11!8!<!000!!/ '*$*%(!-%E$&!%E*!YNH\U[4!<!>V!11!8!!0000!!/ '*$*%(!-%E$&!%E*!Y]U\QH4!<!^V!11!8!_!000!!/ '*$*%(!-%E$&!%E*!Y]QH5M\!<!`V!11!8!a!000!!/ '*$*%(!-%E$&!%E*!TQZ[\!<!;;V!11!bA!000!C '*$*%(!-%E$&!%E*!TNH\U[4!<!;>V!11!!A!000!C! '*$*%(!-%E$&!%E*!T]U\QH4!<!;^V!11!_A!000!C '*$*%(!-%E$&!%E*!T]QH5M\!<!;`V!11!aA!000!Ca '*$*%(!-%E$&!%E*!TN4M\4c4MT!<!;dV!11!8"900/
8"9%-!*)'*<=000=!)E6<=000=/
!!!!000 81"9%-/
%-FFT#$%&'e*%&0)E6%#.E:)E*!<<!)E6J!ff!F*)'*JJ!A!000!C
'*$*%(!-%E$&!%E*!TQR]c4MT!<!;gV!11!81"900/ '*$*%(!-%E$&!%E*!T4MTcQZ[\!<!;hV!11!bA00C
GSP中のgrails tag
さて, Parse#page()を見てみると, <g:...>, </g:...>に対応してそれぞれstartTag(), endTag ()を呼んでいます. Syntax Tagの場合にはGrailsTag#doStartTag(), doEndTag()を呼び 出しています. 一方そうでないタグ (Dynamic Tag) の場合には, 次のような文字列を吐き 出しています.
ここでinvokeTagはorg.codehaus.groovy.grails.web.pages.GroovyPage
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/GroovyPage.java のメソッドです.
パーサParse.javaをもっとよく見てみます. まずコンストラクタがあります. パーサとして の入り口はparse()のようです. 中を読むとどうやら, parse()はGSPページをgroovyコー ドに変換するらしい (この辺はJSPと同じですね).
では, どんなコードに変換されているか, 実際に見てみることにしましょう. 簡単なgrailsア プリケーションを作り, log4jの設定を変えます.
次に適当なドメイン・クラスを作って, ")E)#$*)+$&&して, #BE+$22すると, コンソールに以 下のようなコードが流れるはずです.
都合によりお見せできません. 自分でやってみて下さい.
つまり, JSPと同じく, GSPはGSPページをまずは対応するgroovyコードに変換し, GSP ページ中に埋め込まれたgroovyコード断片を正当なクラスとして構成し, それをコンパイ ルして走らせると, 埋め込まれたgroovyコードも含めて展開 (評価) されたHTMLが文字列 として返される, という仕組みなのですね.
.B*02#%E*&EF=,.@7=i*$"UE@)Ii=!<!E)L!T#..67[$")4$"j.@7F*S%'D,%E@%E"0L),\)kB)'*J!A=!JV .B*02#%E*&EF=C=JV
%-F*:0S$'M**#%,B*)'J!A
!!!!.B*02#%E*&EF=%E6.l)4$"Fm=i*$"R$:)i=mDm=iE'i=mD$**#'=i*$"UE@)Ii=D,.@7=i*$"UE@)Ii=J=JV C!)&')!A
!!!!.B*02#%E*&EF=%E6.l)4$"Fm=i*$"R$:)i=mDm=iE'i=mDn9oD,.@7=i*$"UE@)Ii=J=JV C
8"#$%&'!$22/1"#$%&'+$221(.E-1H.E-%"0"#..67
!!!000
!!!&."`P!A
!!!!!!!000
!!!!!!!.#"!A
!!!!!!!!!!!000
!!!!!!!!!!!(.@)S$B'0"#..670"#$%&'0L),02$")'<=@),B"=!11!!TN[
!!!!!!!!!!!000
!!!!!!!C
!!!!!!!000
!!!C
!!!000
ここでGroovyPage#invokeTag() (org.codehaus.groovy.grails.web.pages. GroovyPage
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/GroovyPage.java ) は以下のようなことをやっているようです.
引数はタグ名, タグの名前空間名, 属性のMap, bodyクロージャ artefactとしてのTagLibを探してきて, 呼び出す
面倒くさくなってきましたが, 更にもう少し遡ってみるとこんな感じです. GroovyPagesServlet
(org.codehaus.groovy.grails.web.pages.GroovyPagesServlet
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/GroovyPagesServlet.java )
servletの受け口
doGet()/doPost() -> doPage() -> renderPageWithEngine() -> GroovyPagesTemplageEngine()#createTemplate()
GroovyPagesTemplateEngine
(org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/GroovyPagesTemplateEngine.java )
GroovyPageMetaInfoを作って (buildPageMetaInfo()), new GroovyPageTemplateを返す
buildPageMetaInfo()では, パースして (前述), パースしたものをコンパイルして (前述), それらを元にGroovyPageMetaInfoを作り, pageCacheに入れる reloadableでない場合 (かつ変更がない場合) にはこのキャッシュを利用している GroovyPageTemplate (org.codehaus.groovy.grails.web.pages.
GroovyPageTemplate
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/GroovyPageTemplate.java )
中身は大してない. 後はresponseとして返すだけ.
GroovyPageMetaInfo (org.codehaus.groovy.grails.web.pages. GroovyPageMetaInfo
(http://svn.grails.codehaus.org/browse/grails/trunk/grails/src/web/org/codehaus/groovy/grails/web/pages/GroovyPageMetaInfo.java )
ページに対応したgroovyコード (前述) とそのメタ情報を持っている
GSPの他の構文要素
grails tag以外のGSPの構文要素についてもざっと眺めておきましょう.
8!<!000!!/はJSPのスクリプトレットと同じで, ...の部分を文字列として, groovyページ (というか, GSPページをgroovyクラスに変換したもの) に埋め込みます. 8!!000!!/, 8!a! 000!!/もJSP由来のもので, 中身も同じようなものです.
8!_!000!!/, _A000C はJSPでいうpage directiveを表しています. とりあえず有効なディレ クティブは, %:2.#*, (.E*)E*472), @)-$B&*H.@)(の三つのようです.
bA000C, !A000C, aA000CはGroovy ScriptletでJSPのスクリプトレットとほぼ同じです. 以上が, grailsのビュー周り, Webプレゼンテーションのレイヤの概要です. ただし, grails でアプリケーションを作る場合には必ずしもGSPを使わなければならないと言うことはあ りません. Wicket (http://grails.org/Wicket+Plugin) やOpenLaszlo
(http://grails.org/OpenLaszlo+plugin) を使うプラグインも提供されています (完成度は おいておいて).
"http://wiki.metabolics.co.jp/index.php/%E3%83%93%E3%83%A5%E3%83%BC" より作成
最終更新 18:25, 2008年1月26日 (日)。
コンテンツはAttribution-NonCommercial-ShareAlike 2.1 Japan Licenseのライセンスで利用することができます。