ikeike443のブログ

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

Scala 2.9.x で Dynamic に XML をゴニョゴニョする

コードが下記なんだけど、うまく動かない。
http://pastie.org/1469213


ので、チョット直してみた。誰かのGistをフォークして直したので、上に関係ないのも入ってるけど気にしない。下半分のDynamicXMLを見てね。(上半分も動くように直したけど)


使うときはscalaかscalacに-Xexperimentalオプションを渡してね。

$ scala -Xexperimental

Cheers!

追記

Play または sbt でこういう Experimental なオプションを試したい場合は、project/Build.scala に下記のように scalacOptions を設定してやれば動くみたい。

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

object ApplicationBuild extends Build {

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

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

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here      
      scalacOptions in Compile += "-Xexperimental"
    )

}

この設定で、 play run でも Type Dynamic が動くようになりました。


早く 2.10 出ないかなー

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にしてやる必要がある

今日覚えたこと

scala> "ab" map Map('a'->'b', 'b'->'c')
res109: String = bc

なぜこれが動くのか。

Map#apply

http://www.scala-lang.org/api/current/scala/collection/Map#apply(A):B

Retrieves the value which is associated with the given key. This method invokes the default method of the map if there is no mapping from the given key to a value. Unless overridden, the default method throws a NoSuchElementException.
key the key
returns the value associated with the given key, or the result of the map's default method, if none exists.
Definition Classes
MapLike → GenMapLike → Function1

上記の通り、MapのapplyはKeyを引数にとってValueを返す。
よって、このapplyメソッドが呼ばれたものと考えられる。

なぜ Map#applyが呼ばれるのか

map の引数は関数であることが要求される。つまり、上記のMapは関数として評価されていると考えられる。
下記と等価。

"ab" map ( Map('a'->'b', 'b'->'c').apply _ )

同じ事を自分で定義したクラスやオブジェクトで出来るか?

下記だとNG。なぜなら関数ではないから。

scala> object a {
     |   def apply(s: Char) = s.toInt
     | }
defined module a
scala> "ab" map a
<console>:9: error: type mismatch;
 found   : a.type (with underlying type object a)
 required: Char => ?
              "ab" map a
                       ^


関数として定義すればいい。下記のように実装すればOK。

scala> object b extends (Char => Int) {
     |   def apply(s: Char) = s.toInt
     | }
defined module b

scala> "ab" map b
res111: scala.collection.immutable.IndexedSeq[Int] = Vector(97, 98)

Mapは関数なの?

はい。
http://www.scala-lang.org/api/current/scala/collection/Map#apply(A):B
上記に書いてあります。Map#applyはFunction1で定義されているものらしい。


という理解をした。

第3回Playframework勉強会 #play_ja をやりました

http://playdocja.appspot.com/assets/images/logo-ja.png

去る7月14日、勉強会をやりました。

下記がその要項です。
http://playframeworkja.doorkeeper.jp/events/1231-%E7%AC%AC3%E5%9B%9Eplayframework%E5%8B%89%E5%BC%B7%E4%BC%9A-play_ja

70人で募集をかけたところ、3時間で満席になってしまいました。本当にありがとうございます。
最終的にはキャンセル待ちの方が75人にまでふくれあがりましたが、実際のところ現場に来場されたのは54人でした。

場所は@i2keyさんのご好意で、リクルートメディアテクノロジーラボさんの超シャレオツな会場を借りることが出来ました。
本当に本当にありがとうございました!

Ust録画

下記の通りバッチリあります。
リクルートメディアテクノロジーラボさんの開場は、いろいろ設備が充実していて驚愕です。
とてもクリアにスライドが録画されている上、音声もマイクでバッチリ拾えているので、この上ない臨場感です!



Video streaming by Ustream



Video streaming by Ustream

当日のタイムテーブルと資料一覧


























発表者タイトル
@ikeike443  Playアップデート
@i2key さん

 MediaTechnologyLabsの紹介


 Play2.0+JavaでAPIサーバを開発しているiPhoneアプリの紹介 


@tototoshi さん  Play20 + Scala について
@kara_d さん

 2.0Javaのオススメと、1.2からのアップグレード


@daiksy さん  Play2.0 + Scala での開発事例

LT分






























発表者タイトル
@garbegetown さん  ドキュメント翻訳について
@masahito さん  Play2.Xドキュメント翻訳について
@seratch さん  plugin 開発の導入
@たけぞう さん  ;Play1.2.X系+Scalaの話
@gakuzzzzさん  Play20-auth の話か DDD on Play20 みたいな話
@puriketu99 さん  おしべめしべシステムからplayをはじめよう


というわけで、またやりましょー。次は4ヶ月以内くらいにこれくらいの規模でまたやりたいです。

SquerylのPostgresqlAdapterはSerial型に対応していないのかな?

PostgresqlのSerial型って、{テーブル名}_{カラム名}_seqという名前のシーケンスを暗黙的に作った上で自動的に内部でnextvalを呼び出してくれるものだと思うのですが、違いましたっけ。

結構理解が適当なので、間違っているのかもしれない。。


ともあれ、SquerylというORMを使っていたところ、シーケンス名を作り出す関数が下記のように実装されていてSerial型に対応できない。

//org.squeryl.internals.DatabaseAdapter.scala
def createSequenceName(fmd: FieldMetaData) =
    "s_" + fmd.parentMetaData.viewOrTable.name + "_" + fmd.columnName


ので、下記のようにオーバーライドしてやった。

class PgAdapter extends PostgreSqlAdapter {
  override def createSequenceName(fmd: FieldMetaData) =
    fmd.parentMetaData.viewOrTable.name + "_" + fmd.columnName + "_seq"
}

ドメインオブジェクトからJSONへのアンマーシャルがだるい

このページの説明を見ててあーダルいなと思いました。

import play.api.libs.json._

case class User(id: Long, name: String, friends: List[User])
  implicit object UserFormat extends Format[User] {
    def reads(json: JsValue): User = User(
      (json \ "id").as[Long],
      (json \ "name").as[String],
      (json \ "friends").asOpt[List[User]].getOrElse(List()))
    def writes(u: User): JsValue = JsObject(List(
      "id" -> JsNumber(u.id),
      "name" -> JsString(u.name),
      "friends" -> JsArray(u.friends.map(fr => JsObject(List("id" -> JsNumber(fr.id), "name" -> JsString(fr.name))))))) 
  }

上記みたいなコードを毎回書くのだるい。

特にアンマーシャルはダルいので、下記みたいなコード書いてみた。


こう呼び出します。as("application/json")がダサいなあ。もっといい方法あれば教えて下さい。

case class User(id: Long, name: String, friends: List[User])
def sendJson() = Action {
    val peter = User(2, "peter",  List())
    Ok(toJson(peter)).as("application/json")
}


マーシャルは、まあまた今度考えよ。。

追記

totoshiさんがlift-jsonを使ってもっとイージーに書けるようにしてくれました。
あざーす。
http://d.hatena.ne.jp/tototoshi/20120526/1338032893

メモ

Scala勉強中


Collectionについていろいろ試してみた。
結論もなければ整理もしてないので悪しからず。。


MapやListに関数を入れられる

scala> val m = Map("func" -> {i:Int=>i*3})
m: scala.collection.immutable.Map[java.lang.String,Int => Int] = Map(func -> <function1>)

でもgetやgetOrElseで取り出すと

scala> m.getOrElse("func", None)
res71: ScalaObject = <function1>
上手く実行できない
scala> m.getOrElse("func", None)(3)
<console>:9: error: ScalaObject does not take parameters
              m.getOrElse("func", None)(3)
                                       ^

どうもOptionで包まれて返ってくる値を上手く取り出すにはMatchを使うのがいいっぽい。

scala> m.get("func") match{
     |   case Some(f) => f(2)
     |   case None => "none"
     | }
res68: Any = 6

こうすると中身を上手く取り出せるが、Any, Some, Optionの意味がまだ分かってない。

ごちゃ混ぜのMapも書ける

scala> val m = Map("func" -> {i:Int=>i*2}, "name" -> "ikeda", 3 -> "number", ("localhost", 9000) -> "Play")
m: scala.collection.immutable.Map[Any,java.lang.Object] = Map(func -> <function1>, name -> ikeda, 3 -> number, (localhost,9000) -> Play)

下記のように書いたら取り出せるかとおもいきやコンパイルエラー。

scala> m.foreach {
     |   case (function1, f) => f(3)
     |   case Some(v) => v
     |   case ((String,Int),name) => name
     |   case None => "none"
     | }

getOrElseで取り出しても、Any,Objectになっちゃうから、関数として取り出せないっぽい?

scala> m.getOrElse("func",0)
res3: Any = <function1>

scala> val f = m.getOrElse("func",0)
f: Any = <function1>

scala> f
res4: Any = <function1>

scala> f(3)
<console>:10: error: Any does not take parameters
              f(3)
               ^

scala> val f:function1 = m.getOrElse("func", {})
<console>:8: error: not found: type function1
       val f:function1 = m.getOrElse("func", {})
             ^

scala> val f:Function1 = m.getOrElse("func", {})
<console>:8: error: trait Function1 takes type parameters
       val f:Function1 = m.getOrElse("func", {})
             ^

scala> val f:Function1[function1] = m.getOrElse("func", {})
<console>:8: error: wrong number of type arguments for Function1, should be 2
       val f:Function1[function1] = m.getOrElse("func", {})
             ^

scala> val f:Function1[Int,Int] = m.getOrElse("func", {})
<console>:8: error: type mismatch;
 found   : Unit
 required: Int => Int
       val f:Function1[Int,Int] = m.getOrElse("func", {})
                                                      ^

scala> val f:Function1[Int,Int] = m.getOrElse("func", None)
<console>:8: error: type mismatch;
 found   : None.type (with underlying type object None)
 required: Int => Int
       val f:Function1[Int,Int] = m.getOrElse("func", None)
                                                      ^

scala> val f:Function1[Int,Int] = m.getOrElse("func", {i:Int=>1})
<console>:8: error: type mismatch;
 found   : java.lang.Object
 required: Int => Int
       val f:Function1[Int,Int] = m.getOrElse("func", {i:Int=>1})
                                             ^

scala> val f:Function1[Int,Int] = m.getOrElse[Function1[Int,Int]]("func", {i:Int=>1})
<console>:8: error: type arguments [Int => Int] do not conform to method getOrElse's type parameter bounds [B1 >: java.lang.Object]
       val f:Function1[Int,Int] = m.getOrElse[Function1[Int,Int]]("func", {i:Int=>1})
                                             ^

scala> m
res6: scala.collection.immutable.Map[Any,java.lang.Object] = Map(func -> <function1>, name -> ikeda, 3 -> number, (localhost,9000) -> Play)

scala> val f:Function1[Int,Int] = m.getOrElse[Function1[Int,Int]]("func", {i:Int=>1})
<console>:8: error: type arguments [Int => Int] do not conform to method getOrElse's type parameter bounds [B1 >: java.lang.Object]
       val f:Function1[Int,Int] = m.getOrElse[Function1[Int,Int]]("func", {i:Int=>1})


map関数

scala> val number = List(1,2,3)
number: List[Int] = List(1, 2, 3)

scala> number.map( (i:Int) => i*2 )
res7: List[Int] = List(2, 4, 6)

これはOK

こうも書ける

scala> val f = (i:Int) => i*2
f: Int => Int = <function1>

scala> number.map(f)
res9: List[Int] = List(2, 4, 6)

こうでも一緒

scala> number.map(f(_))
res12: List[Int] = List(2, 4, 6)

でも、これはNG

scala> number.map(f _)
<console>:10: error: type mismatch;
 found   : () => Int => Int
 required: Int => ?

              number.map(f _)

defで宣言してこうも書ける

scala> def timesTwo(i:Int) = i*2
timesTwo: (i: Int)Int

scala> number.map(timesTwo _)
res11: List[Int] = List(2, 4, 6)

これもOK

scala> number.map(timesTwo(_))
res13: List[Int] = List(2, 4, 6)

defで宣言した時と、無名関数として作った場合とで挙動が違うっぽい。
引数の渡され方の問題?


これ出来るの結構便利

scala> val (x,y) = number.partition(_>2)
x: List[Int] = List(3)
y: List[Int] = List(1, 2)

scala> x
res23: List[Int] = List(3)

scala> y
res24: List[Int] = List(1, 2)


filterやmapにはコードブロックが渡せて、中でCase書ける。なんでかはわからんが。。

scala> val extensions = Map("steve" -> 100, "bob" -> 101, "joe" -> 201)
extensions: scala.collection.immutable.Map[java.lang.String,Int] = Map(steve -> 100, bob -> 101, joe -> 201)


こんなふうにも書けるし。

scala> extensions.filter( (name: (String,Int)) => name._2 < 200 )
res18: scala.collection.immutable.Map[java.lang.String,Int] = Map(steve -> 100, bob -> 101)

これはダメだけど、

scala> extensions.filter( case (name, ex) => ex < 200 )
<console>:1: error: illegal start of simple expression
       extensions.filter( case (name, ex) => ex < 200 )
                          ^

こうすればOK

scala> extensions.filter({ case (name, ex) => ex < 200 })
res19: scala.collection.immutable.Map[java.lang.String,Int] = Map(steve -> 100, bob -> 101)

こうすればmapもできる。

scala> extensions.map({ case(name,ex) => ex*2 } )
res20: scala.collection.immutable.Iterable[Int] = List(200, 202, 402)

mapしたあとタプルで返せば再びMapになる。

scala> extensions.map({ case(name,ex) => name->ex*2 } )
res21: scala.collection.immutable.Map[java.lang.String,Int] = Map(steve -> 200, bob -> 202, joe -> 402)