ikeike443のブログ

ソフトウェアビジネスに関心がある系のブログ

レビュアーとして参加したPlay Frameworkのスターターガイド(洋書)が出ました

Packt PublishingのInstant bookというミニ電子書籍シリーズとして、"Instant Play Framework Starter"という本が出ました。

なぜかレビュアーを依頼されて、まともに英語もしゃべれない身でありながらレビュアーとして参加しました。初めてレビュアーとして参加した書籍が洋書ってのも面白いですね。

献本してくれると言うのでそれをもらってからブログを書こうと思ってたんですが、出版から2日経ってもまだ来ないので先にブログを書いてしまいます。。なのでまだ完成品の中身を見てないので、ほんとに僕がレビュアーとして記載されてるかは分かりませんw

この本はPlay Frameworkに興味をもった入門者向けのハンズオン形式のスターターガイドです。PlayのインストールからCRUDの作成くらいまでをさらっと学べます。ScalaJavaの両方に対応しています。

Playって名前は聞いたことあるけど触ったことないなあ、今度の週末暇だから一日遊んでみるか、っていう人に最も適した本だと思います。

価格も日本円にして約600円くらいと安めですし、KindleでもEpubでも読めるっぽいので、英語が苦じゃない方はどうぞ。

スラッシュ付きとスラッシュなしURLの扱いとPlay2での逃げ方

Play2でスラッシュ付きとスラッシュ無しで別のRouteとして扱われちゃって面倒くさいという問題があったと思いますが。

ここでも議論になっています。

James Roperの言うように、Global.scalaにコード書けば、一応問題は避けられそうです。将来的にはこれはPlay内に機能として持ったほうがいいコードだと思いますが。。

ちなみにこの辺についてどう対応するのが正しいのかについては、下記Googleのサイトが参考になります。(上記チケット中にもリンクあり)
http://googlewebmastercentral-ja.blogspot.jp/2010/11/url.html

Jamesが提示したように、片方を200にして片方を301にするっていうのがあるべき姿みたいですね。

Play2 + Scala のカバレッジツールで一番マシなのは scct かな?

この記事は Play or Scala Advent Calendar 2012 12/10 分の記事です。


使ってる Play のバージョンは 2.0.4 です。Scala のバージョンは 2.9.1 ですよ。

なんかjacoco4sbtだめ

以前jacocoとjenkinsの記事を書いたんですが、jacocoだと余計なカバレッジまで計測してしまう(下記画像参照)ので困っていました。play new して作ったプロジェクトに、 GET / するテストを書いただけなのでカバレッジ100%になるはずなのに、異常にカバー率が低い。。
f:id:ikeike443:20121209040822p:plain
この場合RoutesやらReverseやらのPlayがジェネレートしているコードが邪魔なので、下記のように除外設定を書けば少しはましになりますが。

//Build.scala
    jacoco.excludes in jacoco.Config :=
      Seq(
        "views.html.*",
        "Routes*", 
        "*Reverse*", 
        "views**", 
        "routes*" )

f:id:ikeike443:20121209040746p:plain
でもまだまだダメ。例えばApplication.scalaのレポートをみると、、なんじゃこりゃー。。
f:id:ikeike443:20121209040348p:plain
Controllerトレイトの実装まで全部カバレッジレポートの対象にしちゃうので見にくくてしょうがない。

scctかな?

で、scctを試してみました。
潔いくらいドキュメントがない! のですが、導入は簡単です。機能も少ないし。
ちなみに Usage のところに sbt0.12 以上って書いてあるけど、無視しました。*1

使い方

Build.scalaはこう。

import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {
    val appName         = "coveragesample"
    val appVersion      = "1.0-SNAPSHOT"

    lazy val s = Defaults.defaultSettings ++ Seq(ScctPlugin.instrumentSettings: _*)

    val appDependencies = Seq(
      // Add your project dependencies here,
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA, settings = s).settings(
      // Add your own project settings here      
        parallelExecution in test := false
    )
}

ポイントは、

  • scctの設定を追記する
   lazy val s = Defaults.defaultSettings ++ Seq(ScctPlugin.instrumentSettings: _*)
  • PlayProjectのsettingsプロパティに上記設定をセット(settings=sのところね)
    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA, settings = s)

です。*2

あと、parallelExecution in test := false も忘れない様につけておきましょう。これをつけないとテストが並列で実行されてしまいます。困りますよね? 僕は困ります。*3

で、plugins.sbtあたりにこう。

resolvers += Classpaths.typesafeResolver

resolvers += "scct-github-repository" at "http://mtkopone.github.com/scct/maven-repo"

addSbtPlugin("reaktor" %% "sbt-scct" % "0.2-SNAPSHOT")

これで下記のように実行すればいいです。

$ play scct:test

下記のような結果が得られます。
f:id:ikeike443:20121209041228p:plain

RoutesやReverseが含まれちゃってるのはjacocoのときと変わりませんが、Application.scalaの中を覗くと、余計なものが含まれてなくて綺麗だね!
f:id:ikeike443:20121209041315p:plain

さらにscctさんはcobertura互換のxmlも吐いてくれてます。なのでこいつをJenkinsに食わせましょう。

Jenkinsにセット

手順としては

  • 事前準備:必要な(Jenkinsの)プラグインが入ってなければいれておく

Coberturaプラグインが入ってない場合はいれておきましょう。他にもGitを使うのであればGitのプラグインなどを入れましょう。

  • フリースタイルジョブを作る
  • "設定 > ビルド > シェルの実行" を選択し、下記をセット
/path/to/play-2.0.4/play clean
/path/to/play-2.0.4/play test
/path/to/play-2.0.4/play scct:test

パスは適切に設定してくださいね。
play test と play scct:test と、テストを2回実行しているのには理由があって、play testはJUnitXMLを吐くため、play scct:testはカバレッジレポートを吐き出すため、です。ダサいですね。ごめんなさい。。

  • "ビルド後の処理 > JUnitテスト結果の集計"を設定
  • "ビルド後の処理 > Coberturaカバレッジレポートを集計"を設定

2つ合わせて下記の感じで。
f:id:ikeike443:20121209040235p:plain
Coberturaプラグインのインストールを忘れてると"ビルド後の処理 > Coberturaカバレッジレポートを集計" が出てこないので注意。
その他、SCMからコードを取得してくる設定とかは適宜やってください。

設定を上手いことやって実行すると、下記のような結果が得られます。カバレッジの推移も見れますね。
f:id:ikeike443:20121209041621p:plain

Application.scalaのレポートもバッチリですね。
f:id:ikeike443:20121209041639p:plain

JUnitの結果も集計できています。いつからデグったのかとか、わかり易くていいですね。
f:id:ikeike443:20121209041700p:plain

課題

手が回らなかったけど、いずれ解決したい課題

  • ReverseとかRoutesとかview.htmlとか、アプリコードではないものの除外*4
  • testを2回実行するのはダサいので、scct:testにJUnitXMLを吐かせるかなにかしたい

まとめ

Routes やら Reverse やらのジェネレートされたコードがカバレッジ対象に含まれちゃうのは jacoco と変わりませんが、フレームワークのコード(Controller トレイトなど)が混ざらない分、scctの方がマシそうです。

多分除外設定書くのも難しくないし。

内部では Scala Compiler Plugin として実装されているので、コード読むのも面白いです。
みんなでパッチ送って育てましょう。

2012/12/10 19:00追記

なんか、Playプラグインを使ってると scct:test がうまく動かないみたいです。会社のメンバーが教えてくれました。
play.plugins とか、その辺のファイルがないって怒られるっぽい。
なので、下記のようなスクリプトを会社の人が書いてくれました。play test と play scct:test の間に実行するといいみたいです。

#!/bin/bash

[ -z "$1" ] && echo "Usage: $0 PATH_TO_PLAY_COMMAND" && exit -1

echo "exec: $1 scct:compile"
$1 scct:compile
echo "... copy some files for scct:test"
for i in `find ./target/scala-2.9.1/classes -type f`; do
  TARG=`echo $i | sed -e "s/\/classes\//\/scct-classes\//"`;
  [ -f $TARG ] || echo "Copy $i";
  [ -f $TARG ] || [ -d `dirname $TARG` ] || mkdir -p `dirname $TARG`;
  [ -f $TARG ] || /bin/cp $i $TARG;
done
echo "exec: $1 scct:test"
$1 scct:test

使い方としてはこんな感じ

export PATH=/usr/java/jdk1.6.0_37/bin/:$PATH
/usr/local/play-2.0.4/play clean
/usr/local/play-2.0.4/play test
./scct-test.sh /usr/local/play-2.0.4/play

根本的には、play-scct プラグインを書かないといけないですが、それはまた別の機会に。。(早ければ年末!)

*1:Play2.0.4 は sbt0.11.3 を使用しています

*2:ちなみにこの辺の settings とかの書き方って、いろんな書き方ができるので実は上記のとおりに書かなくてもいいのです。奥が深いというか、sbtを理解すればいろいろ捗るのかもしれないので覚えてみてはいかがでしょうか。

*3:sbtさんは親切でつけてるんだと思うけど、勝手にテストを並列に実行されて嬉しい人いるんだろうか。。それが必要なときはCIサーバを増やしてCIサーバ側(要はJenkins)で並列実行するんだから余計なことしないでいいと僕は思うんだけど。。

*4:これはscct自体に機能がない。既に要望は上がってるんだけど、本人にヤル気がなさそう。なんで必要なのか分からんって、言うけど、Playには必要なんだよ!! https://github.com/mtkopone/scct/issues/31

Salat(play-salat)で日付型のカラムにJodaTimeを使う方法

Play Scala でアプリを作る際、データベースにMongoDBを採用するってのは、結構ある話かなと思います。
PlayでMongoDBを使おうと思うと、play-salat を使うことになると思います。SalatはMongoDB用のORMです。

SalatでJoda Timeを使う方法をメモ書きしておきます。

下記をインポートするといいです。

  RegisterConversionHelpers() 
  RegisterJodaTimeConversionHelpers() 

Salat、というかPlay-Salatを使う場合、下記のようなパッケージオブジェクトを作っておくと思うのですが、

package models

import com.novus.salat.dao._
import com.novus.salat.annotations._
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.commons.conversions.scala.{RegisterConversionHelpers, RegisterJodaTimeConversionHelpers}

import com.novus.salat.{ TypeHintFrequency, StringTypeHintStrategy, Context }
import play.api.Play
import play.api.Play.current

package object mongoContext {
  RegisterConversionHelpers()
  RegisterJodaTimeConversionHelpers() 

  implicit val context = {
    val context = new Context {
      val name = "global"
      override val typeHintStrategy = StringTypeHintStrategy(when = TypeHintFrequency.WhenNecessary, typeHint = "_t")
    }
    context.registerGlobalKeyOverride(remapThis = "id", toThisInstead = "_id")
    context.registerClassLoader(Play.classloader)
    context
  }
}

上記のように、この中に宣言してやるといいです。

あとは各モデルクラスの中で、下記のようにパッケージオブジェクトをインポートしてやればいい感じにJodaTimeを使えます。

package models

import play.api.Play.current
import org.joda.time.DateTime
import com.novus.salat._
import com.novus.salat.annotations._
import com.novus.salat.dao._
import com.mongodb.casbah.Imports._
import se.radley.plugin.salat._

import mongoContext._

case class Hoge(fuga: String, baz: String, date: DateTime)

object Lead extends ModelCompanion[Hoge, ObjectId] {
  val dao = new SalatDAO[Hoge, ObjectId](collection = mongoCollection("Hoge")) {}

  def findOneByVisitorId(fuga: String): Option[Hoge] = dao.findOne(MongoDBObject("fuga" -> fuga))
}

WEB+DB Press vol.71 にPlay framework特別企画記事を書きました

Playの普及を進めるには、メジャーな技術系雑誌に書かないとなあと思い、今年の2,3月くらいから技術評論社さんと企画の相談をしていたのですが、なかなか実現しませんでした。

それが8月に入って急遽15ページいただけることになり、締切りは8月末ってのにちょっとビビったのですが、なんとか一緒に書いてくれる人を探し出して(梅澤さん、九岡さんありがとうございました!)、企画を実現することが出来ました。

そんなWEB+DB Vol.71が今日届きました。


Play特別企画の他、安全堅牢なWebサイトの作り方、WebSocket入門、イクメンエンジニアインタビュー、など、盛りだくさんのVol.71は10月24日発売です。

皆さん是非ご購入ください!

でもって、「Play企画が良かった、もっと詳しく特集してほしい」とか書いてはがきとかメールを送ったりTwitterでつぶやいたりすると、もっと踏み込んだ特集が書けると思います! 宜しくお願いします!!!

WEB+DB PRESS Vol.71

WEB+DB PRESS Vol.71

Play2 Scala + jacoco4sbt + jenkins でカバレッジを取る

Play2 Scalaで作ったプロジェクトをJenkins上で自動テストするのは簡単。フリースタイルジョブで作ってシェルの実行のところに下記のように書いてやれば普通に動く。JUnitスタイルのXMLを吐いてくれるので、テスト結果をJenkins上で綺麗に整形してみることができる。

play clean
play test


でもカバレッジとなると話は別で、Play1の時のようにCoberturaプラグインが用意されてるわけでもなく、上手い方法があるのかどうかよく分からない。

でもって、Scalaカバレッジ計測ができるツールないかなと探してたんだけど、メンテが止まってるのが数個見つかったくらいでいいのがなかった、んだけど、一個いい感じのを見つけた。

jacoco4sbt

https://bitbucket.org/jmhofer/jacoco4sbt/wiki/Home
jacocoの for sbt ということで、sbtプラグインとして書かれている。そこそこメンテもされてるよう。

jacoco4sbtをPlayで動かそう

そのままだと上手く動かないんだけど、下記の記事を参考にすると何とかなる。
http://ronalleva.com/2012/04/25/jacoco-and-play.html

記事の内容としては、jacocoというかsbtはデフォルトだとテストを並列に実行してしまう、けどもPlayは並列実行の設定をオフにしているため、jacocoだけ並列に動こうとして上手くいかないけどどうしたらいいのか、ってもの。

これを見ると、jacoco4sbtをPlayと共存させるための設定方法がよく分かる。(っていうかPeter Hausel*1に聞いた、って記事中にあるね。。)

詳しくは記事を見ればわかると思うけど、参考までに最終的に僕の設定がどうなったかを書いておく。

Build.scala
import sbt._
import Keys._
import PlayProject._
import de.johoop.jacoco4sbt.JacocoPlugin._
import de.johoop.jacoco4sbt.XMLReport
import de.johoop.jacoco4sbt.HTMLReport

object ApplicationBuild extends Build {

  val appName = "Hoge"
  val appVersion = "1.0-SNAPSHOT"

  lazy val s = Defaults.defaultSettings ++ Seq(jacoco.settings:_*)

  val appDependencies = Seq(
    // Add your project dependencies here,
  )

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA, settings = s ).settings( 
    // Add your own project settings here
    // for jacoco
    parallelExecution in jacoco.Config := false,
    jacoco.reportFormats in jacoco.Config := Seq(XMLReport("utf-8"), HTMLReport("utf-8")),
    jacoco.outputDirectory in jacoco.Config := new File("target/coverage/")

    )

}


Build.scalaに上記のように書いてやれば、jacocoに対していろいろ設定できる。jacoco.settingsをsに追記してやって、そのsをPlayProjectの引数として渡してるね。こんなことできるんだ。。

ここでは記事と同じく parallelExecutionをオフにしたほか、カバレッジレポートのスタイルをXMLとHTMLの2本立てにして*2、レポートの出力先をtarget/coverage/に変更した。

plugins.sbt
// Comment to get more information during initialization
logLevel := Level.Warn

// The Typesafe repository 
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"

// Use the Play sbt plugin for Play projects
addSbtPlugin("play" % "sbt-plugin" % "2.0.3")


// jacoco
resolvers += "Sonatype" at "https://oss.sonatype.org/content/groups/scala-tools/"

libraryDependencies ++= Seq(
  "org.jacoco" % "org.jacoco.core" % "0.5.9.201207300726" artifacts(Artifact("org.jacoco.core", "jar", "jar")),
  "org.jacoco" % "org.jacoco.report" % "0.5.9.201207300726" artifacts(Artifact("org.jacoco.report", "jar", "jar"))
)

addSbtPlugin("de.johoop" % "jacoco4sbt" % "1.2.3")

plugins.sbtに、上記のようにjacoco4sdbtの設定を追記。バージョンはこのブログを書いている時点のものなので、適当に読み替えてほしい。


それから、jacocoが吐き出すXMLをJenkinsに解釈させるために、Jenkinsのjacoco Pluginも入れた。

Jenkinsではシェルの実行に下記のような記述をした。

play test
play jacoco:cover

ちなみに、running(TestServer(xxx))を使ってHTTPスタックのテストを書いてカバレッジをとろうとすると上手くいかない。これは今調査中。


Enjoy!

Jenkins

Jenkins

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

*1:Playのコミッターの一人。

*2:jenkinsに読ませるにはXMLにしてやる必要がある