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!
- 作者: John Ferguson Smart,Sky株式会社玉川竜司
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/02/22
- メディア: 大型本
- 購入: 12人 クリック: 304回
- この商品を含むブログ (26件) を見る
Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)
- 作者: 佐藤聖規,和田貴久,河村雅人,米沢弘樹,山岸啓,川口耕介
- 出版社/メーカー: 技術評論社
- 発売日: 2011/11/11
- メディア: 単行本(ソフトカバー)
- 購入: 24人 クリック: 430回
- この商品を含むブログ (50件) を見る
今日覚えたこと
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 をやりました
去る7月14日、勉強会をやりました。
70人で募集をかけたところ、3時間で満席になってしまいました。本当にありがとうございます。
最終的にはキャンセル待ちの方が75人にまでふくれあがりましたが、実際のところ現場に来場されたのは54人でした。
場所は@i2keyさんのご好意で、リクルートメディアテクノロジーラボさんの超シャレオツな会場を借りることが出来ました。
本当に本当にありがとうございました!
Ust録画
下記の通りバッチリあります。
リクルートメディアテクノロジーラボさんの開場は、いろいろ設備が充実していて驚愕です。
とてもクリアにスライドが録画されている上、音声もマイクでバッチリ拾えているので、この上ない臨場感です!
当日のタイムテーブルと資料一覧
発表者 | タイトル |
---|---|
@ikeike443 | Playアップデート |
@i2key さん | MediaTechnologyLabsの紹介 Play2.0+JavaでAPIサーバを開発しているiPhoneアプリの紹介 |
@tototoshi さん | Play20 + Scala について |
@kara_d さん | |
@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)