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 も書かれてないから呼び出し方が間違ってるのかもしれない。
いずれにせよ、情報無さすぎで使わない判断。