2014/9

  • Sep
  • 7
  • 2014

IT

ScalaMatsuri2014で「Xitrum Live Coding」というタイトルで発表をしました。

ScalaMatsuri2014 で「Xitrum Live Coding」というタイトルで発表をしました。


ビデオはこちら
http://live.nicovideo.jp/watch/lv191315534

デモコードはこちら
https://github.com/xitrum-framework/matsuri14


コンパイルエラーとか失敗したりしたけど楽しかったです。ありがとうございました。
ScalaでWebフレームワークというと圧倒的にPlayFrameworkが人気かと思いますが、
Xitrumの知名度が少しでも上がったかなと思います。
また、デモアプリのチャットにはAkkaを使用していますが、
弊社モビルスのKonnect内部ではAkkaと合わせてHazelcastをもっと応用しています。

SparkやDatabricksなど、Konnectのサービスの解析に使えそうなので
勉強したいと思いました。

最後に、モビルスでもScalaエンジニアを募集しています。
興味があるかたはTwitter(@george_osd)などでご連絡いただければと思います。

[Xitrumことはじめ][基本編] 6. レスポンスとビュー: JavaScriptとCSS

Xitrumことはじめ (基本編)

Xitrumことはじめシリーズでは、Xitrumを使ったWebアプリケーション開発を勉強します。

目次はこちら

記事とサンプルコードはMITライセンスでgithubで公開します。

6. レスポンスとビュー:

Viewに関連する項目として今回は、webJarsを利用したフロントエンドのライブラリ活用についてやります。

Scala(Java)プロジェクトにおけるフロントエンドライブラリ管理の手法としてWebJarという仕組みがあります。
Xitrumもこの仕組をサポートしています。

また、Xitrumのコア自体もwebJarを利用していくつかのフロントエンドライブラリを使用しています。

build.sbt

libraryDependencies += "org.webjars" % "jquery" % "2.1.1"
 
libraryDependencies += "org.webjars" % "jquery-validation" % "1.13.0"
 
libraryDependencies += "org.webjars" % "sockjs-client" % "0.3.4"
 
libraryDependencies += "org.webjars" % "swagger-ui" % "2.0.22"
 
libraryDependencies += "org.webjars" % "d3js" % "3.4.11"

webJarを利用することで、
bowerやyoeman、npmなどScala以外のツールの導入なしでプロジェクトのリソース管理を全て
sbtにまとめることができます。

もちろんbowerなどのツールを利用することも、CDNなどを利用することもできますが、
今回はこのwebJarsを利用してみようと思います。

公式ドキュメントは以下のページが参考になります。

6-5. JavaScriptとCSS

webJarを利用するには、使用したいライブラリが公開されている必要があります。
http://www.webjars.org/ から該当のライブラリを探します。

JavaScriptライブラリとしてUnderscore.jsを導入してみます。
またxitrum-newをscaffoldとして使用した場合、CSSライブラリとしてbootstrapが既に導入されています。

プロジェクトのbuild.sbtに該当のライブラリをlibraryDependenciesとして追加します。

build.sbt

libraryDependencies += "org.webjars" % "bootstrap" % "3.2.0"
 
libraryDependencies += "org.webjars" % "underscorejs" % "1.6.0-3"

build.sbtを更新したら、sbtを使用してライブラリをダウンロードします。

sbt/sbt update

以下のように

[info] Resolving org.webjars#underscorejs;1.6.0-3 ...
[info] downloading http://repo1.maven.org/maven2/org/webjars/underscorejs/1.6.0-3/underscorejs-1.6.0-3.jar ...
[info]     [SUCCESSFUL ] org.webjars#underscorejs;1.6.0-3!underscorejs.jar (751ms)
[info] Done updating.

該当のライブラリがダウンロードされます。

これらはjar形式でプロジェクト起動時のクラスパスに含まれる事になります。

アプリケーションのViewから該当のリソースを取得するには、ActionwebJarsUrlを使用します。
テンプレートエンジンにxitrum-scalateを使用している場合、webJarsUrlはテンプレートファイル内でそのまま使えます。

DefaultLayout.jade

link(type="text/css" rel="stylesheet" media="all" href={webJarsUrl("bootstrap/3.2.0/css", "bootstrap.css", "bootstrap.min.css")})
 
script(src={webJarsUrl("underscorejs/1.6.0", "underscore.js", "underscore-min.js")})

webJarsUrlには、

  • 第1引数として該当のjarファイルのパス(META-INF/resources/webjars以下)を指定します。
    jar内のファイル群がどういう構成かは、http://www.webjars.org/ のFilesボタンを押せば分かります。

  • 第2引数には該当のファイルを指定します。

  • 第3引数にはプロダクション環境用のファイルを指定します。通常は圧縮版のファイルを指定します。該当のファイルの圧縮版が存在しない場合は第2引数と同じものを指定します。

ビルドしてDefaultLayoutを使用しているSiteIndexにアクセスしてみます。

http://localhost:8000/

webJarsUrlは以下のリンクに展開されています。

<link type="text/css" rel="stylesheet" media="all" href="/webjars/bootstrap/3.2.0/css/bootstrap.css?4pWKTr6RZtuqbFkxGygQIQ">
 
<script src="/webjars/underscorejs/1.6.0/underscore.js?3ZZjvppx81cLw18O26KHEg"></script>

ブラウザはこれらのタグを解析して、GETリクエストとしてXitrumサーバに送信する事になります。
Xitrumサーバは受け付けたリクエストをどのようにルーティングしているのでしょうか。

少し内部の難しい話になりますが、
XitrumのChannelPipelineにはinboundハンドラーとして
WebJarsServer.scalaがあります。
このハンドラーがwebjarasで始まるURLの場合に、該当のファイルをjarファイルの中から見つけてレスポンスしてくれるという事になります。
該当のリソースが見つからない場合404エラーフラグをセットして次のハンドラーへ処理が移ります。

一方でユーザーが書くアプリケーションのActionへのルーティングは、inboundハンドラーの最後にあります。
そのため、該当のリソースがwebJarsに見つからなければユーザーが書いたルーティングで処理されうるということです。

実験してみましょう。

WebJarsNoRoute.scala

@GET("/webjars/underscorejs/1.6.0/underscore.js", "/webjars/underscorejs/1.7.0/underscore.js")
class WebJarsNoRoute extends Action {
  def execute() {
    respondText("underscorejs-1.7.0 is not found")
  }
}

curl http://localhost:8000/webjars/underscorejs/1.6.0/underscore.js

curl http://localhost:8000/webjars/underscorejs/1.7.0/underscore.js

1つめのリクエストは正しいwebJarsリソースが存在するので、underscore.jsがレスポンスされて、
2つめのリクエストはWebJarsNoRouteが実行されます。


今回はこれまで。
次回は、レスポンスおよびルーティングに関わるトピックの最後として
リダイレクトとフォーワード、ポストバックをやってみます。

[Xitrumことはじめ][基本編] 6. レスポンスとビュー: Viewに関するAPI

Xitrumことはじめ (基本編)

Xitrumことはじめシリーズでは、Xitrumを使ったWebアプリケーション開発を勉強します。

目次はこちら

記事とサンプルコードはMITライセンスでgithubで公開します。

6. レスポンスとビュー:

Xitrum-scalateをテンプレートエンジンに使用することで、
Actionの各メソッドや、現在のActionのインスタンスをViewから使用することができるようになります。
もちろんAction内部でインラインでViewを構成する場合にもAPIが使用できます。
今回はXitrumが提供するViewに関連するAPIを勉強します。

公式ドキュメントは以下のページが参考になります。

6-4. Viewに関するAPI

xitrum.Actionクラスは、
多くのtraitをwithで継承しており、それらはXitrum内部で使用するもの以外にも、
ユーザーアプリケーションから使用することができるAPIとして提供されています。

具体的なAPIの使用例として
ScaffoldProjectのDefaultLayout.jadeが参考になります。

DefaultLayout.jade

- import quickstart.action._
 
!!! 5
html
  head
    != antiCsrfMeta
    != xitrumCss
 
    meta(content="text/html; charset=utf-8" http-equiv="content-type")
    title My new Xitrum project
 
    link(rel="shortcut icon" href={publicUrl("favicon.ico")})
 
    link(type="text/css" rel="stylesheet" media="all" href={webJarsUrl("bootstrap/3.2.0/css", "bootstrap.css", "bootstrap.min.css")})
    link(type="text/css" rel="stylesheet" media="all" href={publicUrl("app.css")})
 
  body
    .container
      h1
        a(href={url[SiteIndex]}) My new Xitrum project
 
      #flash
        !~ jsRenderFlash()
      != renderedView
 
    != jsDefaults
    != jsForView

ここでは以下のAPIが使用されています。

  • antiCsrfMeta
  • xitrumCss
  • publicUrl
  • webJarsUrl
  • url
  • jsRenderFlash
  • renderedView
  • jsDefaults
  • jsForView

    実際に出力されるHTMLは以下の通りです。

    <!DOCTYPE html>
    <html>
     <head>
       <meta name="csrf-token" content="7c8da946-1b53-47de-8a62-b2d361e96510"/>
       <link href="/webjars/xitrum/3.16/xitrum.css?mhIAFrxv3tBMQXtHcoYT7w" type="text/css" rel="stylesheet" media="all"/>
       <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
       <title>My new Xitrum project</title>
       <link rel="shortcut icon" href="/favicon.ico?BjK0shXmVIuSRS0IsYBdHA"/>
       <link type="text/css" rel="stylesheet" media="all" href="/webjars/bootstrap/3.2.0/css/bootstrap.css?4pWKTr6RZtuqbFkxGygQIQ"/>
       <link type="text/css" rel="stylesheet" media="all" href="/app.css?V0CGnmnzXFV6l7a-UkY_7w"/>
     </head>
     <body>
       <div class="container">
         <h1>
           <a href="/">My new Xitrum project</a>
         </h1>
         <div id="flash">
         </div>
          <!-- DefaultLayoutを継承した各ActionのrenderedViewの結果がここに出力される -->
       </div>
             <script type="text/javascript" src="/webjars/jquery/2.1.1/jquery.js?dAMGCVD0oTvjs9_eBJDuBQ"></script>
             <script type="text/javascript" src="/webjars/jquery-validation/1.12.0/jquery.validate.js?MoZJHtxFQR8TR6gNokHx2w"></script>
             <script type="text/javascript" src="/webjars/jquery-validation/1.12.0/additional-methods.js?VMrHLE7MT-YZGBg3T6jSGA"></script>
             <script type="text/javascript" src="/webjars/sockjs-client/0.3.4/sockjs.js?G6ezG627D2WKnJ3F55SoNQ"></script>
             <script type="text/javascript" src="/xitrum/xitrum.js?BMfHCVrVdosDpgtIlbqZWw"></script>
     </body>
    </html>

まず、antiCsrfMetacsrf-tokenというメタタグに展開されています。
このタグはxitrum.js内の処理によって、ajaxリクエスト実行時にリクエストヘッダーに自動で含められることになります。
xitrumはcsrf対策として、put/post/deleteリクエストを受け付けた場合デフォルトでcsrfトークンの妥当性をチェックします。

このtokenは、antiCsrfInputまたはantiCsrfTokenとして取得することも可能です。
後程のサンプルで実際に使ってみます。

なお、Actionでcsrfトークンチェックを無効にしたい場合は、xitrum.SkipCsrfChecktraitを
継承します。

次に、xitrumCssです。
こちらは、単純なCSSファイルへのlinkタグに展開されます。
xitrum.jsを使用している場合に、使われるクラスが定義されています。アプリケーションで不要なら必ずしも記載する必要はありません。

つづいて、urlpublicUrlwebjarsです。
これらはそれぞれ、ActionへのURL,publicディレクトリへのURL、webJarへのURLへと展開されます。
また、publicUrlwebjarsは各リソースのETagに応じてクエリストリングを自動で付加してくれます。
このほか、@sockjsアノテーションに対応したActionのURLを取得するsockJsUrl
@websocketアノテーションに対応したActionのURLを取得する、urlwebSocketAbsUrlなどがあります。
(websocketの場合、ws/wssスキーマも含めたフルパスが取得できます。)

<div id="flash">および、jsRenderFlashはxitrum.jsと組み合わせて、
ちょっとしたアラートを出すための記述になります。
Action内でflashメソッドで追加した文字列が表示されます。
具体的な使用例はのちほど。

jsDefaultsはjQuery、jQuery-validation、sockJS、xitrum.jsが展開されます。
xitrum.js は具体的には
https://github.com/xitrum-framework/xitrum/blob/master/src/main/scala/xitrum/js.scala
が該当します。
xitrum.jsは前述のajaxリクエストに対するcsrf攻撃対策トークンを含めることの他に、
jqueryValidationの実行、postback関連のクライアント処理、flashメッセージ出力処理などが記載されています。
主にクライアントサイドとサーバーサイドの連携、バリデーションを目的とした機能となります。
アプリケーションに応じてこれらの処理が不要であれば、jsDefaultsは必ずしも記載する必要はありません。

jsForViewにはAction内でjsAddToViewで追加したjavaScript文字列が展開されます。

そのほか、ActionからViewへパラメータを渡すatや、i18n文字列を生成するtなどのView用APIがあります。
それではatを使ったサンプルを作ってみたいと思います。

ViewAPIExample.scala

case class Person(name:String,age:Int)
 
@GET("/viewapi/at")
class AtExample extends Action {
  def execute() {
 
    at("key1") = "value"
 
    at("taro") = new Person("Taro", 10)
 
    at("jiro") = new Person("Jiro", 20)
 
    at("serializable") = Map("key" -> "val")
 
    respondView()
  }
}

この例ではリクエストを受け付けた際に、atメソッドを使用して"key1"、"key2"という名前でそれぞれ、
文字列とオブジェクトを保存しています。

AtExample.jade

- import quickstart.action.Person
 
- val key1Value = at("key1").asInstanceOf[String]
- val person = at("taro").asInstanceOf[Person]
- val person2 = at[Person]("jiro")
 
div
  p "key1"
  p = key1Value
 
  p "taro"
  p = person.name
  p = person.age
 
  p "jiro"
  p = person2.name
  p = person2.age
 
  p "toJson"
  p = at.toJson("serializable")

Viewの方では、Scalaコードでatからキーと型を指定して値を取得します。
atの実態は実はHashMap[String, Any]です。(https://github.com/xitrum-framework/xitrum/blob/46f330ac6c360688417406dcc1539ebb8704b721/src/main/scala/xitrum/scope/request/At.scala)
そのため値を取得する際は型を指定する必要があります。

次はJavaScriptに関連するAPIとして、flashjsAddToViewおよびjsForViewメソッドを使ってみます。

ViewAPIExample.scala

@GET("/viewapi/js")
class JsExample extends DefaultLayout {
  def execute() {
 
    flash("Hello World")
 
    jsAddToView("""console.log("Hello js Add To View")""")
 
    jsAddToView("""console.log("Hello js Add To View Again!")""")
 
    respondView()
  }
}

JsExample.jade

p This is a "JsExample" See web console

この例では、リクエストを受け付けると、flashメソッドに"Hello World"という文字列を登録、
その後jsAddToViewメソッドを2回呼び出してそれぞれjavascriptのコードを登録しています。
実際に http://localhost:8000/viweapi/js にアクセスして返されるhtmlには、
DefaultLayout内のjsForViewが以下のように展開されています。

    <script type="text/javascript">
        //<![CDATA[
        $(function() {
        console.log("Hello js Add To View");
        console.log("Hello js Add To View Again!");
        xitrum.flash("Hello World");
        });
        //]]>
    </script>

また、画面は以下の用になります。

と表示されます。
これは最後のxitrum.flashというxitrum.js内のjavascript関数によって生成されたDOMエレメントとなります。

xitrum.flash = function (msg) {
  var div =
    '<div class="flash">' +
      '<a class="flash_close" href="javascript:">X</a>' +
      '<div class="flash_msg">' + msg + '</div>' +
    '</div>';
  $('#flash').append($(div).hide().fadeIn(1000));
}

flashのスタイルはデフォルトのxitrum.cssを使用していますが
アプリケーションに応じてxitrum.flashやクラスのスタイルをオーバーライドすることも可能です。

jsAddToView("""xitrum.flash = function(msg){alert("This is custome flash:" + msg);}""")

この他にもViewに関するAPI(i18nなど)はいくつかありますが、今回はここまで。
次回は、JavaScript、CSSについてもう少し詳しく見ていきます。