Upload
tomohito-ozaki
View
1.776
Download
5
Embed Size (px)
Citation preview
Liftで Live Coding!
自己紹介
• 尾崎 智仁 • ID:yuroyoro • フリーエンジニア • Javaとか
Blog
今日の内容
1. Liftの紹介 2. Tropyを作ってみよう! 3. Liftって実際どう?
Liftの 紹介
Liftの特徴 その1 • ScalaのフルスタックWebフレームワーク
• 最近のフレームワークのいいとこ取り
• Scalaの関数型言語や型推論に加え、既存のjava資産が利用できる。
Liftの特徴 その2
• インストールやサーバの起動はrailsみたいにMavenコマンド一発。
• データベースも最初からついてる(derby)
Liftの特徴 その3
• Snipetアプローチ
(View First) • コントローラが中心ではなく、Viewを中心としたアーキテクチャ
• テンプレートはXHTML。独自の名前空間のXMLタグとSnippetを結合
• Wicketのようなコンポーネントが可能に
Liftの特徴 その4
• ActiveRecordみたいなモデル
+ DjangoのAdmin • モデルとデータベースのマッピングはCoCで簡単に記述
• 簡単にCRUDフォームを作成
Liftの特徴 その5
• Ajaxサポート • JQuery同梱。
• Ajaxによる呼び出しはajaxCallメソッドでシンプルに実現
• サーバサイドから簡単にJavascriptを実行。
Liftの特徴 その6
• Commetを簡単・スケーラビリティに
• 設定はCocとScalaコード。XMLなし。
• SiteMapやACLを最初から組み込んである。
• アプリケーションのテンプレートを使えばユーザ管理、サインアップなどが組み込まれた状態で開発できる。
Liftのアーキテクチャ
Model
LiftFilter
LiftServlet
Boot
LiftRule
LiftSession
RequestState
Snippet
HTML Template
Requestを受けるfilter
URLのDispatchなどのルール
DBなどの動作環境の設定情報
Reqeustの種類(Commet,Ajax)による振り分け
Sessionを扱うRequestの処理。
RequestとResponseの情報。
XHTMLでのテンプレート
Viewへの出力やModelの操作など
KeyedMapper
KeyedMetaMapper ORMでのtrait
CRUD関連処理を 継承
フォームの入力値 などを参照
Modelの操作
XHTMLタグでSnippetを 埋め込む
URLなどからTemplateを検索
URLによる 振り分け
Tropyを 作ってみよう!
Tropyってなに?
• Tropy (とろぴぃ)
• 結城浩さんが作った
• Web0.5 • Wikiのように誰でも追加・更新
• ページの一覧や検索はなし
• ページ移動はランダムのみ
Tropyってなに?
ランダムで移動
誰でも追加・編集
では、やってみよう!
Tropyを作ろう! – プロジェクト作る
Mavenでコマンドうつ
mvn archetype:create -U \
-DarchetypeGroupId=net.liftweb \
-DarchetypeArtifactId=lift-archetype-basic \
-DarchetypeVersion=0.9 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=com.yuroyoro.liftropy \
-DartifactId=liftropy
Tropyを作ろう! – Jettyを起動
Mavenでコマンドうつ
mvn Jetty:run
→ Jettyが立ち上がり
Tropyを作ろう! – Eclipseへ
Mavenでコマンドうつ
mvn eclipse:eclpse
→ Eclipseのプロジェクトに変換
mvn compile
→ コンパイル
Tropyを作ろう! – Eclipseへ
Eclpseにインポート
起動構成
-RunWebAppを
メインクラス
Tropyを作ろう! – Modelを作る
一件分のEntryのModel
package com.yuroyoro.liftropy.model
import net.liftweb.mapper._
object TropyEntry extends TropyEntry with KeyedMetaMapper[Long,TropyEntry]{ }
class TropyEntry extends KeyedMapper[Long ,TropyEntry]{
def getSingleton = TropyEntry def primaryKeyField = tropyId
object tropyId extends MappedLongIndex(this)
object entry extends MappedTextarea(this, 8192) { override def textareaRows = 10 override def textareaCols = 50 }
}
src/main/scala/com/yuroyoro/liftropy/model/TropyEntry.scala
Tropyを作ろう! – Createテンプレートを作る
Createページのテンプレート
<lift:surround with="default" at="content"> <p>Create</p>
<lift:snippet type="TropySnippet:create" form="POST"/> <hr/> <ul> <li><a href="/">Random</a></li> </ul>
</lift:surround>
src/main/webapp/create.html
Tropyを作ろう! – Snippet.createを作る
Createページを出力するSnippet
package com.yuroyoro.liftropy.snippet
import scala.xml.{NodeSeq,Text,Group} import scala.Math._
import net.liftweb.http._ import net.liftweb.http.S._ import net.liftweb.util._ import com.yuroyoro.liftropy.model.TropyEntry
class TropySnippet { private object selectedEntry extends RequestVar[Can[TropyEntry]](Empty)
def create(xhtml:Group) :NodeSeq = { <center> <table> {selectedEntry.is.openOr( new TropyEntry).toForm( Empty , saveEntry _ )} </table> <ul> <li><a href="/">Cancel</a></li> <li><input type="submit" value="Create"/></li> </ul> </center> }
def saveEntry( entry:TropyEntry) = entry.validate match { case Nil => entry.save ; redirectTo("/show/" + entry.tropyId) case x => error(x);selectedEntry(Full(entry)) }
}
src/main/scala/com/yuroyoro/liftropy/snippet/TropySnippet.scala
Tropyを作ろう! – Boot.scalaを修正 Boot.scalaにModelのMigrateと CreateページのPathを登録する
class Boot { def boot { if (!DB.jndiJdbcConnAvailable_?) DB.defineConnectionManager(DefaultConnectionIdentifier, DBVendor) // where to search snippet LiftRules.addToPackages("com.yuroyoro.liftropy")
Schemifier.schemify(true, Log.infoF _, TropyEntry)
// Build SiteMap val entries = Menu(Loc("Random", "/", "Random")) :: Menu(Loc("create","/create","create")) ::Nil LiftRules.setSiteMap(SiteMap(entries:_*))
} }
src/main/scala/bootstrap/liftweb/Boot.scala
Tropyを作ろう! – showテンプレートを作る
showページのテンプレート
<lift:surround with="default" at="content"> <p><lift:TropySnippet.show /></p> <hr/> <ul> <li><a href="/create">Create</a></li> <li><lift:TropySnippet.editLink /></li> <li><a href="/">Random</a></li> </ul> </lift:surround>
src/main/webapp/show.html
Tropyを作ろう! – Snippet.showを作る
showページを出力するSnippet
def show(xhtml:Group) :NodeSeq = S.param("tropyId") match { case Empty => getRandomTropyId match{ case 0 => redirectTo("/create") case tropyId => redirectTo("/show/" + tropyId) } case tropyId => TropyEntry.findByKey( tropyId.open_!.toLong) match{ case Empty => redirectTo("/") case entry => selectedEntry(entry); <p>{entry.open_!.entry}</p> } }
def getRandomTropyId : Long ={ val rand = new Random() TropyEntry.count match { case 0 => 0 case x => (abs(rand.nextLong) % x) + 1 } }
def editLink(xhtml:Group) :NodeSeq = <a href={"/edit/" + selectedEntry.open_!.tropyId}>Edit</a>
src/main/scala/com/yuroyoro/liftropy/snippet/TropySnippet.scala
Tropyを作ろう! – Boot.scalaを修正 Boot.scalaにModelのMigrateと ShowページのPathを登録する
val entries = Menu(Loc("Random", "/", "Random")) :: Menu(Loc("create","/create","create")) :: Menu(Loc("edit", "/edit", "edit")) :: Menu(Loc("show", "/show", "show")) :: Nil
src/main/scala/bootstrap/liftweb/Boot.scala
/show/<tropyId>のようなURLで、 <tropyId>をパラメータとして扱う LiftRules.addRewriteBefore { case RewriteRequest(ParsePath( “index"::Nil, _,_), _, _) => RewriteResponse("show"::Nil) case RewriteRequest(ParsePath( "show"::tropyId :: _ , _ ,_ ), _, _) => RewriteResponse("show"::Nil , Map("tropyId" -> tropyId)) case RewriteRequest(ParsePath( "edit"::tropyId :: _ , _ ,_ ), _, _) => RewriteResponse("edit"::Nil , Map("tropyId" -> tropyId)) }
Tropyを作ろう! – editテンプレートを作る
editページのテンプレート
<lift:surround with="default" at="content"> <p>Edit</p>
<lift:snippet type="TropySnippet:edit" form="POST"/> <hr/> <ul> <li><a href="/create">Create</a></li> <li><a href="/">Random</a></li> </ul>
</lift:surround>
src/main/webapp/edit.html
Tropyを作ろう! – Snippet.editを作る
editページを出力するSnippet
def edit(xhtml: Group): NodeSeq = S.param("tropyId") match { case Empty => getRandomTropyId match{ case 0 => redirectTo("/create") case tropyId => redirectTo("/show/" + tropyId) } case tropyId => TropyEntry.findByKey( tropyId.open_!.toLong) match{ case Empty => redirectTo("/create") case entry => { selectedEntry(entry); <center> <table> {entry.open_!.toForm(Empty, saveEntry _) } </table> <ul> <li><a href={"/show/" + selectedEntry.open_!.tropyId}>Cancel</a></li> <li><input type="submit" value="Edit"/></li> </ul> </center> } } }
src/main/scala/com/yuroyoro/liftropy/snippet/TropySnippet.scala
Tropyを作ろう! – Template修正
Templateファイルを修正
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/liftropy.css" type="text/css" media="all" />
<title>Liftropy</title>
<script id="jquery" src="/classpath/jquery.js" type="text/javascript"></script>
</head>
<body>
<h1>Liftropy</h1>
<hr/>
<lift:msgs/>
<lift:bind name="content" />
</body>
</html>
src/main/webapp/templates-hidden/default.html
Tropyを作ろう! – 完成!
Liftって 実際どう?
実際にやってみて – よかったさがし • Scalaのパウアはすばらしい
(ファーストクラス関数、型推論、パターンマッチ)
• XMLリテラルはよい
• ORMよく出来ている(今後に期待)
• Jettyあげっぱなしでサクサク開発
• MLが活発(追いきれません…)
実際にやってみて – disるよ • ドキュメント不足(ソース嫁)
• APIとか直感的じゃない
• Snippetアプローチは正直設計やりにくい
• コードにHTMLが混ざる
• Model.toFormが<td>吐くとかねーよ
• Scala 2.7.2に未対応(!?)
まとめ
• 実案件への採用は時期尚早
• ScalaとWebアプリケーションの相性Good!
• WebFlavor期待age
ご清聴 ありがとう ございました