Scala2.10.3 から Redis 弄ってみた
scala-redis
https://github.com/debasishg/scala-redis
Maven のURLも無いし、ドキュメントはそのままビルドしてそうな感じだったので、clone して publpish-local して使った。
Apache2 ライセンスなので、ソース取り込むのが正解かもしれない。jar 配布って出来たかは知らんけど。
import com.redis._ object Sample extends App { val client = new RedisClient("localhost", 6379) val isSet = client.set("SampleString", "SampleMessage") println(isSet) val result = client.get("SampleString") println(result) }
まず、set の内部実装が
def set(key: Any, value: Any)(implicit format: Format): Boolean = send("SET", List(key, value))(asBoolean)
なので、おおよそ Format を実装すれば複雑な型もいけると推測できる
これのデフォルト実装が
object Format { def apply(f: PartialFunction[Any, Any]): Format = new Format(f) implicit val default: Format = new Format(Map.empty) def formatDouble(d: Double, inclusive: Boolean = true) = (if (inclusive) ("") else ("(")) + { if (d.isInfinity) { if (d > 0.0) "+inf" else "-inf" } else { d.toString } } } class Format(val format: PartialFunction[Any, Any]) { def apply(in: Any): Array[Byte] = (if (format.isDefinedAt(in)) (format(in)) else (in)) match { case b: Array[Byte] => b case d: Double => Format.formatDouble(d, true).getBytes("UTF-8") case x => x.toString.getBytes("UTF-8") } def orElse(that: Format): Format = Format(format orElse that.format) def orElse(that: PartialFunction[Any, Any]): Format = Format(format orElse that) }
なので、Format(f:Any => String)
なシリアライザを書けば割とどうとでもなると。っていうか Generics 使いなさいよと思わなくない。
で、 get は
def get[A](key: Any)(implicit format: Format, parse: Parse[A]): Option[A] = send("GET", List(key))(asBulk)
で、parse が入ってくるわけだが、Parse の定義は下記
object Parse { def apply[T](f: (Array[Byte]) => T) = new Parse[T](f) object Implicits { implicit val parseString = Parse[String](new String(_, "UTF-8")) implicit val parseByteArray = Parse[Array[Byte]](x => x) implicit val parseInt = Parse[Int](new String(_, "UTF-8").toInt) implicit val parseLong = Parse[Long](new String(_, "UTF-8").toLong) implicit val parseDouble = Parse[Double](new String(_, "UTF-8").toDouble) } implicit val parseDefault = Parse[String](new String(_, "UTF-8")) val parseStringSafe = Parse[String](xs => new String(xs.iterator.flatMap{ case x if x > 31 && x < 127 => Iterator.single(x.toChar) case 10 => "\\n".iterator case 13 => "\\r".iterator case x => "\\x%02x".format(x).iterator }.toArray)) } class Parse[A](val f: (Array[Byte]) => A) extends Function1[Array[Byte], A] { def apply(in: Array[Byte]): A = f(in) }
バイナリで返ってくるんですね。
いずれにせよバイト配列を、デシリアライズできればよい。
配列的に突っ込むのは別IFらしく
client.lpush(key, value1, value2, ...)(implicit Format) client.rpush(key, value1, value2, ...)(implicit Format) client.lpop(key)(implicit Format, Parser) client.rpop(key)(implicit Format, Parser)
もう突っ込まないよ
object Sample extends App { val client = new RedisClient("localhost", 6379) import serialization._ import Parse.Implicits._ client.hmset("hashMapSet", Map("field1" -> 32, "field2" -> 64)) println( client.hmget[String, Int]("hashMapSet", "field1", "field2") ) }
ただし、Map[String, Any] はうまく走ってくれなかった。まぁ仕方ないか、、、。
非同期取得もあるけど、基本そのまま動く。
scala-redis-nb
何かまだアルファ版もいいとこらしい。
https://github.com/debasishg/scala-redis-nb
name := "RedisSample" version := "1.0" scalaVersion := "2.10.3" resolvers += "spray" at "http://repo.spray.io/" libraryDependencies ++= Seq( "net.debasishg" %% "redisreact" % "0.3", "io.spray" %% "spray-json" % "1.2.5" )
として、公式ほぼ写経で
import akka.actor.ActorSystem import akka.util.Timeout import com.redis.RedisClient import scala.concurrent.duration._ case class User(id:Int, name:String, age:Int) case class Group(id:Int, name:String, users:List[User]) object Sample extends App { implicit val system = ActorSystem("redis-client") implicit val executionContext = system.dispatcher val client = RedisClient("127.0.0.1", 6379) import spray.json.DefaultJsonProtocol._ import com.redis.serialization.SprayJsonSupport._ implicit val childClass = jsonFormat3(User) implicit val groupClass = jsonFormat3(Group) val sampleValue = User(2, "delick", 28) client.set("key", sampleValue) import scala.concurrent.Await implicit val timeout = Timeout(5000) val result = Await.result(client.get[User]("key"), 5 seconds) println(result) system.shutdown() }
としてみた所、Nullpo落ち。
[error] (run-main) java.lang.NullPointerException java.lang.NullPointerException at akka.pattern.AskableActorRef$.ask$extension(AskSupport.scala:136) at com.redis.api.StringOperations$class.set(StringOperations.scala:23) at com.redis.RedisClient.set(RedisClient.scala:26) at Sample$delayedInit$body.apply(Sample.scala:24) /* 中略 */ [trace] Stack trace suppressed: run last compile:run for the full output.
呼び出しに関して、明らかに素の Akka にないような呼び出ししてるし、import も書かれてないから呼び出し方が間違ってるのかもしれない。
いずれにせよ、情報無さすぎで使わない判断。