play2 の scala template code reading
play-2.1.0/framework/src/play/src/main/scala/views/helper/inputText.scala.html
をコードリーディング
@**
* Generate an HTML input text.
*
* Example:
* {{{
* @inputText(field = myForm("name"), args = 'size -> 10, 'placeholder -> "Your name")
* }}}
*
* @param field The form field.
* @param args Set of extra attributes.
* @param handler The field constructor.
*@
@(field: play.api.data.Field, args: (Symbol,Any)*)(implicit handler: FieldConstructor, lang: play.api.i18n.Lang)
@inputType = @{ args.toMap.get('type).map(_.toString).getOrElse("text") }
@input(field, args.filter(_._1 != 'type):_*) { (id, name, value, htmlArgs) =>
<input type="@inputType" id="@id" name="@name" value="@value" @toHtmlArgs(htmlArgs)>
}
1行目:2つの引数、field と argsをとる。 field は、play.api.data.Field型 args は、Symbol型 と Any型のタプルの可変長引数(アスタリスクは可変長引数のこと)
( http://www.atmarkit.co.jp/ait/articles/1204/05/news126_3.html )
また、2つの引数、handler と langをもつ部分関数でカリー化している。 handler は、FieldConstructor型 lang は、play.api.i18n.Lang型 (※implicit (暗黙型)は返り値に適用?引数に適用?)
2行目:変数 inputType に、typeの値を代入 typeの値は、args 配列を mapに変換(toMap)し、keyが 'type のvalueを取得(get) し、もしマッチしたら文字型に変換(toString)して代入。 もしマッチしなかったら(getOrElse)、 "text"を代入。
3行目:inputヘルパーを呼び出し 第一引数は、field 第二引数は、args 配列から タプルの一番目の引数(.1)が 'type に一致しないもの(!='type)。 args.filter(.1 != 'type) :* は可変数引数の配列を関数に渡す場合に付ける。
argsは、タプルのリスト(具体的にはList[(Symbol, Any)]) だが、Scalaの関数の引数にListを渡すという事は、ポインタ渡しではなく実体渡しなので、実質的に可変長引数渡しになるのであろう(たぶん)
http://www.h7.dion.ne.jp/~samwyn/Scala/function.htm
.1 の最初のアンダースコア() は、配列の要素が毎回入る。(アンダースコアはケースのワイルドカード)。二番目のアンスコは、.1(ドットアンスコ1)で、タプルの1番目の要素にアクセス。
* (アンスコアスタリスク)は、シーケンスパターンのワイルドカードで、シーケンス内に残ったすべての要素にマッチ。 今回は、input ヘルパー関数の第二引数であり、「:*」は、関数の引数に可変個の引数を渡す場合につけられる。
input ヘルパーはカリー化されており、部分関数として inputに、 (String, String, Option[String], Map[Symbol,Any]) の4つ outputに、Html を返す関数を受け取る
ここでは inputに、 (id, name, value, htmlArgs) の4つ outputに、<input type="@inputType" id="@id" name="@name" value="@value" @toHtmlArgs(htmlArgs)> の Html を返す関数を渡している。
そして、inputヘルパーの中身
play-2.1.0/framework/src/play/src/main/scala/views/helper/input.scala.html
@**
* Prepare a generic HTML input.
*@
@(field: play.api.data.Field, args: (Symbol, Any)* )(inputDef: (String, String, Option[String], Map[Symbol,Any]) => Html)(implicit handler: FieldConstructor, lang: play.api.i18n.Lang)
@id = @{ args.toMap.get('id).map(_.toString).getOrElse(field.id) }
@handler(
FieldElements(
id,
field,
inputDef(id, field.name, field.value, args.filter(arg => !arg._1.name.startsWith("_") && arg._1 != 'id).toMap),
args.toMap,
lang
)
)
1行目:2つの引数、field と argsをとる fieldは、play.api.data.Field 型 argsは、Symbol型 と Any型 のタプルの配列
また、 inputに、 (String, String, Option[String], Map[Symbol,Any]) の4つ outputに、Html を返す部分関数でカリー化
さらに、2つの引数、handler と langをもつ部分関数でカリー化している。 handler は、FieldConstructor型 lang は、play.api.i18n.Lang型 (※implicit (暗黙型)は返り値に適用?引数に適用?)
2行目:変数 id に、idの値を代入 idの値は、args 配列を mapに変換(toMap)し、keyが 'id のvalueを取得(get) し、もしマッチしたら文字型に変換(toString)して代入。 もしマッチしなかったら(getOrElse)、 field(第一引数)のidを代入。
3行目:inputText 呼び出し時に、部分関数で渡された handler 引数に パラメータを渡してここで実行される handlerは、FieldConstructor 型
FieldConstructorとは?
http://www.playframework-ja.org/documentation/2.0.2/ScalaFormHelpers より、Scalaのテンプレートシステムとして、デフォルトのフィールドコンストラクタとして、 以下の6つのシンボル(と値のタプル)を引数にとる関数が存在する。
'label -> "Custom label" 'id -> "idForTheTopDlElement" 'help -> "Custom help" 'showConstraints -> false 'error -> "Force an error" 'showErrors -> false
FieldConstructor の実体
play-2.1.0/framework/src/play/src/main/scala/views/helper/Helpers.scala
47 trait FieldConstructor extends NotNull {
48 def apply(elts: FieldElements): Html
49 }
50
51 object FieldConstructor {
52
53 implicit val defaultField = FieldConstructor(views.html.helper.defaultFieldConstructor.f)
54
55 def apply(f: FieldElements => Html): FieldConstructor = new FieldConstructor {
56 def apply(elts: FieldElements) = f(elts)
57 }
58
59 implicit def inlineFieldConstructor(f: (FieldElements) => Html) = FieldConstructor(f)
60 implicit def templateAsFieldConstructor(t: Template1[FieldElements, Html]) = FieldConstructor(t.render)
61
62 }
FieldElements の実体
9 case class FieldElements(id: String, field: play.api.data.Field, input: Html, args: Map[Symbol, Any], lang: play.api.i18n.Lang) {
10