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