View First で始める Scala Liftフレームワーク

普通Webサイトを作るというときは画面から作ると思いますが、ことWebアプリケーションとなるとそうではないことが多いのではないでしょうか。

それというのも、Railsをはじめとする昨今のフレームワークでは、画面に至るにはControllerが必要ですしModel定義がないと記述できないこともままあります。

・・・まず簡単に画面を作らせてくれ。そこからModelの構成や画面遷移を検討したいんだ。

そんな心の声を実現するフレームワーク、それがLiftなのです。

# Lift フレームワークの 1 ・ 2 ・ 3
ここでLiftの紹介をしているとそれだけで終わってしまうので、それは[こちら](http://qiita.com/icoxfog417/items/8ac0ac21434fd229395f)を見ていただくとして、ここでは実際にLiftを使ったアプリケーションの作成方法を紹介します。そのStepは以下の通り。

1 初期設定
2 Viewを作る
3 Modelを作る

チュートリアル用のコードは[こちら](https://github.com/icoxfog417/lift_tutorial)。ソース中には関連する情報へのリンクなども記載するようにしました。

## 1.初期設定
まずScalaとそのビルドツールであるsbtがないと話が始まらないので、まずこれらをセットアップします・・・が、これだけでもえらく長いのでここは3分クッキングばりにすでにできているものとして話を始めます。
→Windowsの方は、[セットアップ方法をまとめた記事](http://qiita.com/icoxfog417/items/31d88358942d8dd0eb8b)を書きましたのでご参考ください。

そして公式サイトにサンプルが置いてあるので、これをダウンロードしてきます。
http://liftweb.net/download
[LiftのGithub](https://github.com/lift)にもリポジトリがあるので、こちらからcloneするのでもOKです。

あとはサイトに書いてある通り、試しにダウンロードしたフォルダ内にあるlift_basicでsbtコマンドを実行し、`container:start`を実行すれば`http://localhost:8080`にサイトが立ち上がるはずです。

## 2.Viewを作る
ここからがLiftの本領発揮、まずは画面を作ります。
Viewで出番になるのは、主に以下のフォルダ内のファイルです。

src/main/webapp
src/main/scala/code/snippet

src/main/scala/code/view というもっともらしいフォルダがありますが、基本的にこいつの出番はありません。[example](https://github.com/lift/examples/blob/master/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala)を見るとページを直接レンダリングする際に使うみたいですが(Response.writeでタグを打っていくイメージ)、そんな機会はほぼないと思います。

基本的にはwebapp配下のhtmlファイルでレイアウトを整え、サーバーサイドでの処理が必要な部分をsnippetで記載する、といったイメージになります。

### a.templateを作る
templateなどはwebapp配下のtemplate-hiddenに格納します。
ちなみに、Liftでは`-hidden`がついたフォルダ、`.`や`_`で始まるテンプレートファイルはHTTPアクセスを発生させないでロードされるようです([こちら](http://demo.liftweb.net/templating/surround))。

ページのベースとなるようなhtml、共通で使いたいメニューやフッターといった部品はこの中に格納しておきます。

### b.Viewを作る
作成したテンプレートを使いながらViewを作成します。テンプレートの呼び出し方法は、主に以下２つです(その他は[こちら](https://www.assembla.com/spaces/liftweb/wiki/Templates_and_Binding))。

*Surround*
要素を囲う、逆に言えばtemplateの中に埋め込むような感じになります。
指定方法は以下のようにclassへ指定を行います。

```
<body class="lift:content_id=main">
  <div id="main" class="lift:surround?with=default;at=content">
    default.html内のid="content"に埋め込まれます
  </div>
</body>
```

Surroundされる側は、上記のように`class="lift:content_id=main"`と埋め込まれるcontentを指定しておく。

*Embed*
要素を追加します。こちらは、`what`でファイル名のhtmlを除いた部分を指定します。

```
<span class="lift:embed?what=_copywrite"></span>
```

```html:_copywrite.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lift:content_id="copywrite">
  <head><meta content="text/html; charset=UTF-8" http-equiv="content-type" /></head>
  <body >
  	<span id="copywrite">Copyright 20xx - 20yy All Rights Reserved.</span>
  </body>
</html>
```

### c.snippetを作る
サーバーサイドで処理をしたい箇所についてはsnippetを作成します。ControllerはないのでPOSTなどを受け取るFORM処理もsnippetで実装しますが、最初は一旦Modelや永続化の方法は脳内から消し去り、画面の動きをhtmlで記述することに注力します。

*c1.snippetの呼び出し*

```html
<span class="lift:helloWorld.howdy">
 Welcome to your Lift app at <span id="time">Time goes here</span>
</span>
```

HTML側の記載は、CSS classに`lift:クラス名.メソッド名`で呼び出すsnippetを記載します。
注目すべきは`<span id="time">Time goes here</span>`と書いてある箇所ですが、ここは実際にはsnippetで処理されて置き換えられる部分です。
これが何を意味しているかというと、「snippetの実装がまだ済んでいない箇所はサンプルデータ・タグをいれておくことができるし、実装後もそれを消す必要はない」という点です。
これにより、サーバーサイドの実装はいったん忘れてデザインに集中することができます。

```scala
class HelloWorld {
  lazy val date: Box[Date] = DependencyFactory.inject[Date] // inject the date

  // replace the contents of the element with id "time" with the date
  def howdy = "#time *" #> date.map(_.toString)
}
```

snippet側はこちら。`src/main/scala/code/snippet`に突っ込んでいきます(snippetが検索されるフォルダは、Boot.scalaの`LiftRules.addToPackages("code")`で定義されています)。

注目すべきは `#>` という見慣れない演算子ですが、こいつはjQueryのSelectorのようなものです。
引数として渡されるのはclassが指定されたDOMですが、その中から処理をしたい部分をidやclassを指定して抜き出すことができます。この場合は「id=timeの要素全部」が対象になります。
その種類については[こちら](https://www.assembla.com/wiki/show/liftweb/binding_via_css_selectors)参照。

*c2.snippetでのリクエストの処理*
フォームなどによるリクエスト処理は以下のように行います。

```html
<form class="lift:TodoView.add?form=post">
  <input type="text" name="title" class="column span-10"/>
  <input type="submit" value="Add Todo!" style="margin-top:5px;"/>
</form>
```

```scala
def add(form: NodeSeq): NodeSeq = {
  var title = ""

  def addTodo() = {
    var todo = new Todo(title)   
    todo.validate match{
      case Nil => todo.save(); S.notice("Added " + todo.title)
      case x => S.error(x); S.mapSnippet("TodoView.add", doBind)
    }
  }
  
  def doBind(form: NodeSeq): NodeSeq = {
    var sel = 
        "name=title" #> SHtml.onSubmit(title = _) &
        "type=submit" #> SHtml.onSubmitUnit(addTodo) ;
        return sel(form)
    }
  
  doBind(form)
    
}

```

リクエスト処理は、JavaScriptのコールバックのような形で記載を行います。
scala側の`doBind`で、submit時に受け取る値と処理関数(この場合`addTodo`)を指定しており、実際の処理はこの`addTodo`内で行っています。

つまり、Liftにおけるフォーム処理snippetは、1.サーバーサイドの変数/関数とDOMのバインド、2.実際処理、の2つを実装するのが基本(のはず)です。

*c3.Ajax処理*
Ajaxの簡単なサンプルはこちらになります([Lift cookbook](http://cookbook.liftweb.net/#AjaxFormProcessing)より)。

```html
<form data-lift="form.ajax">
  <div data-lift="EchoForm">
    <input type="text" name="name" placeholder="What's your name?">
    <input type="submit">
  </div>
</form>

<div id="result">Your name will be echoed here</div>
```

```scala
object EchoForm extends {
  def render = {
    var name = ""
    def process() : JsCmd = SetHtml("result", Text(name))

    "@name" #> text(name, s => name = s) &
    "type=submit" #> ajaxSubmit("Click Me", process)
  }
}
```

`ajaxSubmit`をバインドしているように、ここまでは基本的にフォーム処理と同じです(`ajaxText`のように、あらかじめAjaxイベントが発生する要素をレンダリングすることも可能です。また、イベントに合わせて`"button [onclick]" #> SHtml.ajaxInvoke(callback)`のように記載することもできます)。

ただ、Ajax処理の場合コールバック処理の帰り値は`NodeSeq`でなく`JsCmd`である必要があります。上記では`def process() : JsCmd = SetHtml("result", Text(name))`の部分になりますが、これは該当IDの部分を置き換えるJavaScript処理を返却・・・という意味合いになります。使用可能なJsCmdは[API](http://liftweb.net/api/26/api/#net.liftweb.http.js.JsCmds$)の通りなので、ここを見つつ実装する感じになります。

この辺りは実装しているとJavaScriptの方が断然楽、というところがいくつもあるので、棲み分けを考える必要があると思います(サーバーサイド処理を呼び出すだけのTemplateを作って、jQuery Ajaxで処理する、Jsonでやり取りするなど・・・)。


## Modelを作る
LiftにはRailsにおけるActiveRecordのような便利野郎はいないので、もうちょい実テーブルに近い形で記述します。これは一見デメリットのようにも見えますが、実際のテーブルがあまり隠蔽されずちゃんと扱ってる感があるというメリットもあります。

### DB設定
DBへアクセスを行うModelを作成するためには、いろいろと設定が必要なのでまずそれを行います。

*mapperライブラリを追加*
Modelを定義するにあたりmapperのライブラリが必要なため、以下のような感じでこいつを`build.sbt`に追加します。また、DBドライバも当然必要なのでそちらも追加(今回はh2dbのドライバを設定)。

```
libraryDependencies ++= {
  val liftVersion = "2.6-M2"
  Seq(
    "net.liftweb"       %% "lift-webkit"        % liftVersion        % "compile",
    "net.liftweb"       %% "lift-mapper"        % liftVersion        % "compile",
    "net.liftmodules"   %% "lift-jquery-module_2.6" % "2.5",
・・・
    "com.h2database"    % "h2"                  % "1.3.167"
```

Eclipseを使っている場合は、sbtを実行後eclipseコマンドで依存関係をEclipse側のプロジェクトに反映する。

*DBドライバの追加*
当然DBへ接続できないと話にならないので、その設定を追加します。こうしたプロパティ関係は`src/main/scala/bootstrap/liftweb/Boot.scala`へ記載します。
この設定はちょっと長めなので、lift_basicの中の[こちら](https://github.com/lift/lift_26_sbt/blob/master/scala_210/lift_basic/src/main/scala/bootstrap/liftweb/Boot.scala)をご参照ください。


### Modelを作成する
LiftでのModel定義は以下のように行います。ポイントとしては、Mapperを継承してModel classを作成し、それを扱う(findしたりsaveしたりする)オブジェクトをMetaMapper traitを使用して作成するという点です。

MapperはLongKeyedをはじめ複数の種類があり、キーの形態に合わせて使います。
Mapper内の項目はMappedStringなどの項目用のクラスを使って定義していきます。これはちょうど「オブジェクト上の項目(メンバ)とDB上のカラムをMapしている」感じになります。
→バリデーションや型定義などもここへ記載していきます。

種類と使い方はちょっと書ききれないので、詳細は[こちら](https://www.assembla.com/wiki/show/liftweb/Mapper)、また[APIドキュメント](http://liftweb.net/api/26/api/#package)をご参照ください。

```scala
object Todo extends Todo with LongKeyedMetaMapper[Todo]{
  override def dbTableName = "todos" // define the DB table name

  // add static method (like findById etc ...)
}

class Todo extends LongKeyedMapper[Todo] with IdPK {
  def getSingleton = Todo
	
  object todo extends MappedString(this, 100) {
    override def validations = valMaxLen(256, "message must be under 256 characters long ") _ :: super.validations
  }
  object done extends MappedBoolean(this)
	 
  object minutes extends MappedDecimal(this,0) {
    override def defaultValue = 5
  }	 
}
```

Modelの作成が終わったら、Boot.scalaにDBテーブルとの連動設定を追記します。
`Schemifier.schemify(true, Schemifier.infoF _, Todo)`

※scalaにはJavaで言うところのstaticがありません。クラス間で共通の変数はobjectで宣言してそこにまとめることになってます(objectはシングルトンとして扱われる)。クラスと同盟のオブジェクトはコンパニオンオブジェクトと呼ばれます。