reactive-web: Lift をシンプルに

Javascript

JsEventStream

reactive-web には、クライアントサイド内部に完全に収まる FRP の反応 (reaction) をセットアップする機能が用意されています。これをコントロールするのが JsEventStream クラスです。JsEventStream は対応する javascript オブジェクトのプロキシとなるもので、fireforeachmapflatMap、および filter といった一般的なイベントストリームメソッドを呼び出すことができます。scala でこれらのメソッドを呼び出すと、対応する javascript メソッドの呼び出しがブラウザでトリガされます。もちろん、ほとんどの場合、プログラマがこれらのメソッドを呼び出すのはイベントストリームを初期化するときだけで、あとは適切に回路がつながって動作するので、以後はブラウザとサーバーとの間で通信を行う必要はありません。唯一の例外は fire で、これはあとで呼び出すことに意味があり、イベントストリームに値を挿入することでブラウザの動作をコントロールすることができます。

一方、サーバー側で JsEventStream に応答する必要がある場合は、toServer メソッドを呼び出すと、通常の EventStream が返されます。値は、ブラウザに送信されるときに、JSON 形式にエンコードされます。toServer には JValue=>U 関数を渡すことができます。ここで、U は新しい EventStream の型です (JValuelift-json の AST 表現)。toServer は、net.liftweb.json.FormatsManifest[U] で暗黙的に提供することもでき、その場合は lift-json が extract を行います。

Javscript DSL

fireforeachmapflatMap、および filter は、javascript で実際に実行されるプロキシメソッドなので、容易に javascript にレンダリング可能な値を指定してこれらのメソッドを呼び出す必要があります。この操作を簡単にするため、reactive-web には、Scala でタイプセーフな javascript の式と文を記述するための DSL が含まれています。以下にその要点を示します。

以下のスニペットは、DSL で Javascript の文を書く方法を簡単に示したものです。Javascript ブロックで囲むと、その部分がブラウザに送られて実行されます。

Javascript {
  // if / else if / else
  If(true) {
    window.alert("True")
  }.ElseIf (false){
    window.alert("False")
  } Else {
    If(true) {
    } Else {
    }
  }
  // while, do / while
  While(true) {
    window.alert("Again!")
  }
  Do {
    window.alert("Hello!")
  } While (false)
  // switch / case
  Switch(1)(
    0.$ :> {
      window.alert("No")
    },
    1.$ :> window.alert("Yes")
  )
  // 変数の命名
  object i extends JsVar[JsNumber]
  // 通常の for ループ
  For(List(i := 1), i < 10, List(i := i + 1)) {}
  // for / in -- $[JsArray[_]] に対する通常の scala for 内包表記であることに注意
  for (i <- List(1.$, 2.$, 3.$)$) {
    If(i > 1) {
      window.alert("Greater")
    }
  }
  // for each / in (インデックスを使わずに要素に対して繰り返し処理)
  for (i <- Each(List(1.$, 2.$, 3.$))) {
    If(i > 1) {
      window.alert("Greater")
    }
  }
  // throw, try / catch / finally
  Try {
    Throw("message")
  } Catch { c =>
  } Finally {
  }
  object myFunc extends Function({ x: $[JsNumber] =>
    If(x > 10) {
      window alert "Greater"
    } Else {
      window alert "Small"
    }
  })
  myFunc(10)
}

JsStub

reactive-web は、Javascript API スタブを scala のトレイトとして定義できる強力なメカニズムを備えています。このことを利用すれば、プロキシオブジェクトのメソッドを呼び出すだけで、scala のコードから Javascript API を「呼び出す」ことができます。

方法は簡単です。JsStub を継承するトレイトを記述し、このトレイトが表す Javscript オブジェクトの名前を付け、オブジェクトのメソッドに対応する抽象メソッドをトレイトで定義します。メソッドの引数は JsExp である必要があり、JsExp または別の JsStub インタフェースを返すことができます。こうしておいて、$$[theTrait] を呼び出してインスタンスを取得します。

sealed trait obj extends JsStub {
  def method(s: $[JsString]): $[JsString]
  def self: obj
  val prop: Assignable[JsNumber]
}
val obj = $$[obj]
Javascript {
  obj.method(obj.method("This is a scala string"))
  val v = JsVar[JsObj] := obj.self
  obj.prop := 2
}