RをScalaから呼び出し、Eclipseのspecs2でテストする

以外と面倒だったのでメモ

環境
  • Mac OS X 10.10
  • eclipse 4.4
手順 

Rのインストール

brew install r

scalaプロジェクトの作成

$ tree
.
├── build.sbt
├── lib
├── project
│		├── build.properties
│		└── plugins.sbt
└── src
		├── main
		│	 ├── resources
		│	 └── scala
		└── test
				├── resources
				└── scala

rjavaを使うためのライブラリをコピー

$ cp -rp ~/Library/R/3.2/library/rJava/jri/JRI.jar lib
$ cp -rp ~/Library/R/3.2/library/rJava/jri/libjri.jnilib lib

テストを作成

import org.specs2.mutable._
import org.specs2.runner.JUnitRunner
import org.junit.runner.RunWith
import org.rosuda.JRI.{ REXP, Rengine }

@RunWith(classOf[JUnitRunner])
class RTest extends Specification {
	"r " should {
		"r check" in {
			val engine = new Rengine(Array("--no-save"), false, null)
			val result = engine.eval("sum(c(1, 2, 3, 4))")
			engine.end()
			println(result.asDouble)
			result.asDouble mustEqual 10.0
		}

	}
}

build.sbt

name := "sample"

version := "1.0"

scalaVersion := "2.11.6"

javaOptions in run += "-Djava.library.path=" + System.getProperty("java.library.path") + ":lib"


libraryDependencies ++= Seq(
		"org.specs2" %% "specs2-core" % "3.6.2" % "test",
		"org.specs2" % "specs2-junit_2.11" % "3.6.2"
)

scalacOptions in Test ++= Seq("-Yrangepos")
 
retrieveManaged := true	// lib_managedへjarをコピーする

plugin.sbt

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

build.properties

sbt.version=0.13.8

eclipse用変換

sbt eclipse
Rの設定
install.package("rJava")
Eclipseの設定
  1. Eclipseを起動し、プロジェクトを取り込む
  2. プロジェクトのプロパティからjava build path > libraries > add jarsでJRI.jarを追加
  3. JRI.jarの左三角マークをクリックしnative library locationにlibディレクトリを追加
  4. RTest.scalaを右クリックから、Debug Configuration > environment にR_HOMEを設定
R_HOME=/usr/local/Cellar/r/3.2.1_1/R.framework/Resources

これで実行できます

sbtプロジェクトを作成しeclipseでテストする

ちょっとハマったのでメモ

sbtプロジェクトを作成したのちに、eclipse上でJunitテストをできる環境を構築します。

Eclipseで右クリックからの scala Junit Testができるので非常に便利 

まずディレクトリおよびファイルを以下の構成で作成します

$ tree
.
├── build.sbt
├── project
│ ├── build.properties
│ └── plugins.sbt
└── src
		├── main
		│ ├── resources
		│ └── scala
		│		└── Model.scala
		└── test
				├── resources
				└── scala
						└── ModelSpec.scala

8 directories, 5 files

次にファイルの中を記述します

  • build.sbt
name := "sample"

version := "1.0"

scalaVersion := "2.11.6"

javaOptions in run += "-Djava.library.path=" + System.getProperty("java.library.path") + ":lib"


libraryDependencies ++= Seq(
		"org.specs2" %% "specs2-core" % "3.6.2" % "test",
		"org.specs2" % "specs2-junit_2.11" % "3.6.2"
)

scalacOptions in Test ++= Seq("-Yrangepos")
 
retrieveManaged := true	// lib_managedへjarをコピーする
  • build.properties
sbt.version=0.13.8
  • plugins.sbt
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
  • Model.scala
case class Model(ival:Int,sval:String)
  • ModelSpec.scala
import org.specs2.mutable._
import org.specs2.runner.JUnitRunner 
import org.junit.runner.RunWith 


@RunWith(classOf[JUnitRunner]) 
class ModelSpec extends Specification {
	"model" should {
		"value check" in {
			val model=new Model(1,"s")
			model.ival mustEqual 1
			model.sval mustEqual "s"
		}
	}
}

Eclipseようにファイルプロジェクトファイルを作成してやります 

$ sbt eclipse

.project .classpath .settingsが作成され、依存するjarファイルがlib_managedに取り込まれいるのが確認できます

このままeclipseで読み込んで、ModelSpec.scalaを右クリックからのScalaJunitTestでテストできます。

Application.scala

package controllers

import play.api._
import play.api.mvc._
import java.io._
import org.apache.batik.dom.svg._
import org.apache.batik.transcoder._
import org.apache.batik.transcoder.image._
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Document
import java.awt.Rectangle
import org.apache.fop.svg.PDFTranscoder

import play.api.libs.iteratee._
import play.api.data._
import play.api.data.Forms._

case class Image(
	filename:String,
	scale:Int,
	imagetype:String,
	svg:String
)

object Application extends Controller {

	val imageDataForm = Form(
			mapping(
					"filename"->text,
					"scale"->number,
					"type"->text,
					"svg"->text
			)(Image.apply)(Image.unapply)
	)
	
	def index()=Action{implicit request =>
		Ok(views.html.index())
	}

	def makeImage()=Action{implicit request =>
		val image=imageDataForm.bindFromRequest.get
		
		val is=new ByteArrayInputStream(image.svg.getBytes())
		import scala.concurrent.ExecutionContext.Implicits._
		Ok.chunked(Enumerator.outputStream { os =>
			val tin=new TranscoderInput(is)	
			val tot=new TranscoderOutput(os)	
			getTranscoder(image.imagetype, 0.7f) match{
				case Some(x) =>{
					println("transcode:x="+x)
					x.transcode(tin,tot)
				}
				
				case _ => None			
			}
			os.flush
			os.close	
			println("flush")
		} >>> Enumerator.eof).withHeaders(
				"Content-Type" -> (image.imagetype),
				"Content-Disposition"->("attachment; filename="+image.filename)
		)	 
	}


	def getParameter(request:play.api.mvc.Request[play.api.mvc.AnyContent],str:String) = {
		request.queryString.get(str).flatMap(_.headOption) match {
			case Some(x) => x
			case None => ""
		}
	}
	
	def getDocument(is:InputStream):Document ={
				val parser = XMLResourceDescriptor.getXMLParserClassName()
				val f = new SAXSVGDocumentFactory(parser)
				val doc = f.createDocument("http://www.w3.org/2000/svg",is)
				return doc
	}
 
	def	getTranscoder( transcoderType:String,	keyQuality:Float ) :Option[Transcoder]={
		println("getTranscoder:"+transcoderType)
				transcoderType match{
					case "image/jpeg" =>Option(getJPEGTranscoder(keyQuality))
					case "image/png" => Option(getPNGTranscoder())
					case "application/pdf" => Option(new PDFTranscoder())
					case	_ => None
				}
		}
	
	def	getJPEGTranscoder( keyQuality:Float):JPEGTranscoder= {
				val jpeg = new JPEGTranscoder()
				jpeg.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, keyQuality)
				return jpeg
		}
 
		def	getPNGTranscoder() :PNGTranscoder={
				return new PNGTranscoder();
		}
}

index.scala.html





こんな感じでいけるかと思います

scalaのRedisクライアントbrandoを使ってみる(その3)

PlayFramework2.2でscalaのRedisクライアントbrandoを使用する際にはちょっと注意が必要です。

brando1.0はakka2.3対応らしく、Play2.2ではまだakka2.2の対応となっているためそのままでは動きません。

仕方がないのでbrando0.3にバージョンを落として使用します。

また前回のサンプルだと、キーに値がない場合にはエラーになってしまいます。

そこでbrando0.3を少し改造します

  • brand.Brando.scala
		case Tcp.Received(data) ⇒
			parseReply(data) { reply ⇒
				reply match {
					case Some(List(Some(x: ByteString), Some(channel: ByteString), Some(message: ByteString))) if (x.utf8String == "message") ⇒

						val pubSubMessage = PubSubMessage(channel.utf8String, message.utf8String)
						getSubscribers(channel).map { x ⇒
							x ! pubSubMessage
						}
//ここから
					case None =>
						requesterQueue.dequeue ! Option(ByteString(""))
//ここまで追加
					case _ ⇒
						requesterQueue.dequeue ! (reply match {
							case Some(failure) if failure.isInstanceOf[Status.Failure] ⇒ failure
							case success ⇒ success
						})
				}
			}

これで使いやすくなりました

scalaのRedisクライアントbrandoを使ってみる(その2)

前回brandoを使用してRedisの値をGETできたので今回はSubscribeしてみます。

Redisサーバ側から値をプッシュし、それをPlayのWebsocketでブラウザ側に流すため用に特化しています

試行錯誤をかなりしましたが意外と単純でした。

ポイントは起動するクラスをActorとして作成し、PubSubMessageをReceiveするだけです


import akka.actor._
import akka.util._
import scala.concurrent._
import brando._


class MyPubSub extends Actor{
		implicit val timeout= Timeout(5000)
		val system=ActorSystem("brando")
		val brando = system.actorOf(Brando("localhost", 6379,None,None))	 

		brando ! Request("SUBSCRIBE","key")
		def receive={
			 case msg:PubSubMessage=>println("msg="+msg)
			 case _ => 
		 }
}

object PubSub {
	def main(args: Array[String]): Unit = {
		val system=ActorSystem("a")
		system.actorOf(Props(new MyPubSub()))
	}
}

これだけでした。

scalaのRedisクライアントbrandoを使ってみる

ちょっとはまったのでメモ。

scalaのRedisクライアントには何種類か有ります。

http://redis.io/clients

当初scala-redisを使っていたのですが、Pub/Subする際に、内部的にスレッドを生成しているようで、大量のPub/Subを作成したい場合(数千)にはOutOfMemoryで落ちてしまいます。

そこでActorモデルのbrandoを使用してみました。

ネット上にはほとんど情報もなくちょっとはまってしまったので記録しておきます

https://github.com/chrisdinn/brando

こちらからダウンロードしてsbt packageでJarを作成します。

あとakkaが必要なので2.3をダウンロードしておきます。

いろいろと試行錯誤し以下のコードでとれました

Pub/Subとかこれから確かめます

package brandoTest

import akka.actor._
import akka.util._
import akka.pattern.ask
import scala.concurrent._
import brando._

object Main {

	def main(args: Array[String]): Unit = {
		implicit val timeout= Timeout(5000)
		val system=ActorSystem("brando")
		val brando = system.actorOf(Brando("localhost", 6379,None,None))
		val future=brando.ask (Request("GET","key")).mapTo[Some[ByteString]]
		val res=Await.result(future,timeout.duration)
		res match{
			case Some(x)=>println(x.decodeString("UTF-8"))
		}

	}
}

Scala+SlickでTupple22問題を解決してみた

slickというかscalaには有名なタプル22問題があります。

23項目以上のメンバを持ったcase classが作れない。

どうやらscala2.11では解消されているようですが、まだベータ版ということで使うのにはためらいます

そこでネストしたcase classを用いて23カラム以上持つデータベースのテーブルを取得するものを作成してみました

  • scala2.10.2
  • slick2.10-1.0.1
  • eclipse 4.3
  • Tests.scala

import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession
import scala.slick.jdbc.{GetResult, StaticQuery => Q}
import Q.interpolation

	 case class PartA(a01: Int, a02: Int, a03: Int, a04: Int, a05: Int, a06: Int, a07:Int, a08:Int, a09:Int, a10:Int)
	 case class PartB(a11: Int, a12: Int, a13: Int, a14: Int, a15: Int, a16: Int, a17:Int, a18:Int, a19:Int, a20:Int)
	 case class PartC(a21: Int, a22: Int, a23: Int, a24: Int)
	 case class Whole(id: Int, p1: PartA, p2: PartB, p3: PartC)

		object Tests extends Table[Int]("test"){
			def id = column[Int]("id", O.PrimaryKey)
			def a01 = column[Int]("a01")
			def a02 = column[Int]("a02")
			def a03 = column[Int]("a03")
			def a04 = column[Int]("a04")
			def a05 = column[Int]("a05")
			def a06 = column[Int]("a06")
			def a07 = column[Int]("a07")
			def a08 = column[Int]("a08")
			def a09 = column[Int]("a09")
			def a10 = column[Int]("a10")
			def a11 = column[Int]("a11")
			def a12 = column[Int]("a12")
			def a13 = column[Int]("a13")
			def a14 = column[Int]("a14")
			def a15 = column[Int]("a15")
			def a16 = column[Int]("a16")
			def a17 = column[Int]("a17")
			def a18 = column[Int]("a18")
			def a19 = column[Int]("a19")
			def a20 = column[Int]("a20")
			def a21 = column[Int]("a21")
			def a22 = column[Int]("a22")
			def a23 = column[Int]("a23")
			def a24 = column[Int]("a24")

			def * = id

			def all = (
				id,
				(a01, a02, a03, a04, a05, a06, a07, a08, a09, a10),
				(a11, a12, a13, a14, a15, a16, a17, a18, a19, a20),
				(a21, a22, a23, a24)
			)
	
			private def connectDB[Any](db:javax.sql.DataSource,f: => Any): Any = {
				Database.forDataSource(db) withSession {f}
			} 
			implicit val sqlToClass=GetResult(rs=>Whole(rs<<,PartA(rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<),PartB(rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<,rs<<),PartC(rs<<,rs<<,rs<<,rs<<)))
 
			def select(db:javax.sql.DataSource,id:String):Option[Whole]= {
				connectDB(db,
				{
				sql"select 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4 ".as[Whole].list.headOption
				}
		)
	}
}

  • TestsTest.scala

import javax.sql._
import javax.naming._

import org.junit._

import com.mysql.jdbc.jdbc2.optional._

class StockMasterTest {
	 private def testDataSource:javax.sql.DataSource={
	System.setProperty(Context.INITIAL_CONTEXT_FACTORY,"org.apache.naming.java.javaURLContextFactory")
	System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming")
	val ic = new InitialContext()
	ic.createSubcontext("java:")
	ic.createSubcontext("java:comp")
	ic.createSubcontext("java:comp/env")
	ic.createSubcontext("java:comp/env/jdbc")

		val ds = new MysqlConnectionPoolDataSource()
	ds.setUser("root")
	ds.setPassword("password")
	ds.setDatabaseName("dbname")
	ds.setServerName("localhost")
	ds.setPortNumber(3306)
	ic.bind("java:comp/env/jdbc/database", ds)
	ds
	} 

	@Test
	def select:Unit={
		val list=Tests.select(testDataSource,"id");
		println(list)

	}

}
  • eclipse4.3で実行
Some(Whole(0,PartA(1,2,3,4,5,6,7,8,9,0),PartB(1,2,3,4,5,6,7,8,9,0),PartC(1,2,3,4)))

とりあえず動いているようです