Skip to content

Commit

Permalink
Merge branch 'wip-uuid'
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Gurnell committed Nov 22, 2012
2 parents b498faf + e348801 commit 90f56f4
Show file tree
Hide file tree
Showing 14 changed files with 244 additions and 60 deletions.
4 changes: 2 additions & 2 deletions core/src/main/scala/bigtop/concurrent/FutureImplicits.scala
Expand Up @@ -6,8 +6,8 @@ import akka.dispatch.{Await, Future, Promise}
import akka.util.{Duration, Timeout}
import bigtop.problem._
import blueeyes.bkka.AkkaDefaults
import scalaz.{Validation, Success, Failure}
import scalaz.syntax.validation._
import scalaz._
import scalaz.Scalaz._

trait FutureImplicits extends AkkaDefaults {
type FvProblem[A] = FutureValidation[Problem, A]
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/bigtop/concurrent/FutureValidation.scala
Expand Up @@ -3,8 +3,8 @@ package concurrent

import akka.dispatch.{Future, Promise}
import blueeyes.bkka.AkkaDefaults
import scalaz.Validation
import scalaz.syntax.validation._
import scalaz._
import scalaz.Scalaz._

case class FutureValidation[F, S](val inner: Future[Validation[F, S]])
extends FutureImplicits with AkkaDefaults
Expand Down
Expand Up @@ -42,6 +42,13 @@ trait RequestParameterImplicits {
implicit def buildUuid(str: String): Validation[Problem,Uuid] =
Uuid.parse(str).toSuccess(malformed("uuid", str))

implicit def buildBoolean(str: String): Validation[Problem,Boolean] =
str.toLowerCase match {
case "true" => true.success[Problem]
case "false" => false.success[Problem]
case _ => malformed("boolean", str).fail[Boolean]
}

implicit def buildInt(str: String): Validation[Problem,Int] =
parseInt(str).toSuccess(malformed("int", str))

Expand Down
15 changes: 10 additions & 5 deletions core/src/main/scala/bigtop/http/SyncService.scala
Expand Up @@ -73,20 +73,25 @@ object SyncService extends HttpRequestHandlerCombinators with AkkaDefaults {
path(prefix) {
path("/'id") {
get {
logAndProcess("read", read)
// logAndProcess("read", read)
read
} ~
put {
logAndProcess("update", update)
// logAndProcess("update", update)
update
} ~
this.delete {
logAndProcess("delete", delete)
// logAndProcess("delete", delete)
delete
}
} ~
get {
logAndProcess("search", search)
// logAndProcess("search", search)
search
} ~
post {
logAndProcess("create", create)
// logAndProcess("create", create)
create
} ~
logAndProcess(
"not found",
Expand Down
29 changes: 23 additions & 6 deletions core/src/main/scala/bigtop/json/JsonFormatters.scala
Expand Up @@ -77,7 +77,7 @@ trait JsonFormatters {
// Atomic Values ---------------------

implicit val UuidJsonFormat: JsonFormat[Problem,Uuid] = new JsonFormat[Problem,Uuid] {
def write(in: Uuid) = JString(in.name)
def write(in: Uuid) = JString(in.toString)
def read(json: JValue) =
json -->? classOf[JString] map (_.value) flatMap (Uuid.parse _) toSuccess (malformed("json", "uuid", json))
}
Expand Down Expand Up @@ -114,12 +114,29 @@ trait JsonFormatters {
}

implicit val DateTimeJsonFormat: JsonFormat[Problem,DateTime] = new JsonFormat[Problem,DateTime] {
def write(in: DateTime) = JString(Iso8601Format.write(in))
def read(json: JValue) =
json -->? classOf[JString] map (_.value) toSuccess (malformed("json", "time", json)) flatMap (Iso8601Format.read(_)) match {
case Failure(msg) => (malformed("json", "time", json)).fail
case Success(s) => s.success
def write(in: DateTime) = {
// JInt(in.getMillis)
JString(Iso8601Format.write(in))
}

def read(json: JValue) = {
json match {
// case JInt(bigInt) =>
// new DateTime(bigInt.toLong).success[Problem]

// case JDouble(bigDecimal) =>
// new DateTime(bigDecimal.toLong).success[Problem]

case JString(str) =>
Iso8601Format.read(str) match {
case Failure(str) => malformed("json", "time", json).fail[DateTime]
case Success(date) => date.success[Problem]
}

case _ =>
malformed("json", "time", json).fail[DateTime]
}
}
}

implicit val IntJsonFormat: JsonFormat[Problem,Int] = new JsonFormat[Problem,Int] {
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/bigtop/problem/StringValidation.scala
Expand Up @@ -6,8 +6,8 @@ import blueeyes.bkka.AkkaDefaults
import bigtop.concurrent._
import bigtop.problem._
import scala.util.matching.Regex
import scalaz.Validation
import scalaz.syntax.validation._
import scalaz._
import scalaz.Scalaz._

trait StringImplicits {
implicit def stringToStringValidation(str: String) =
Expand Down
15 changes: 10 additions & 5 deletions core/src/main/scala/bigtop/util/Uuid.scala
@@ -1,21 +1,26 @@
package bigtop
package util

import java.util.UUID
import scala.util.matching.Regex
import scalaz.Validation
import scalaz.syntax.validation._
import blueeyes.json.JsonAST._
import blueeyes.json.JsonDSL._

case class Uuid(val name: String) {
override def toString: String = name
}
// case class Uuid(val name: String) {
// override def toString: String = name
// }

object Uuid {
private val UuidRegex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$".r

def create() =
Uuid(java.util.UUID.randomUUID.toString)
def create = UUID.randomUUID

def apply(name: String) = UUID.fromString(name)

def unapply(uuid: Uuid): Option[String] =
Some(uuid.toString)

def parse(name: String): Option[Uuid] =
name match {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/bigtop/util/package.scala
@@ -0,0 +1,5 @@
package bigtop

package object util {
type Uuid = java.util.UUID
}
28 changes: 14 additions & 14 deletions project/Build.scala
Expand Up @@ -77,7 +77,7 @@ object BigtopBuild extends Build {
)

lazy val core = Project(
id = "bigtop-core",
id = "core",
base = file("core")
).settings(
Project.defaultSettings ++
Expand All @@ -88,39 +88,39 @@ object BigtopBuild extends Build {
) : _*
)

lazy val user = Project(
id = "bigtop-user",
base = file("user")
lazy val redis = Project(
id = "redis",
base = file("redis")
).settings(
Project.defaultSettings ++
blueeyesSettings ++
publishSettings ++
Seq(
libraryDependencies += twitterUtil
libraryDependencies += redisclient
) : _*
).dependsOn(core)
).dependsOn(user, core)

lazy val util = Project(
id = "bigtop-util",
base = file("util")
lazy val user = Project(
id = "user",
base = file("user")
).settings(
Project.defaultSettings ++
blueeyesSettings ++
publishSettings ++
Seq(
libraryDependencies ++= Seq(specs2, metricsCore, metricsScala)
libraryDependencies += twitterUtil
) : _*
).dependsOn(core)

lazy val redis = Project(
id = "bigtop-redis",
base = file("redis")
lazy val util = Project(
id = "util",
base = file("util")
).settings(
Project.defaultSettings ++
blueeyesSettings ++
publishSettings ++
Seq(
libraryDependencies += redisclient
libraryDependencies ++= Seq(specs2, metricsCore, metricsScala)
) : _*
).dependsOn(core)

Expand Down
76 changes: 76 additions & 0 deletions redis/src/main/scala/bigtop/user/RedisSessionActions.scala
@@ -0,0 +1,76 @@
package bigtop
package user

import bigtop.json._
import bigtop.util._
import bigtop.problem._
import bigtop.concurrent._
import bigtop.concurrent.FutureImplicits._
import com.redis._
import com.redis.serialization.{
Format => RedisFormat,
Parse => RedisParse
}
import com.weiglewilczek.slf4s.Logging
import scalaz.syntax.validation._
import scalaz.std.option.optionSyntax._

trait RedisSessionActions[U <: User] extends SessionActions[U] {

def userActions: UserActions[U]
def sessionFormat: JsonFormat[Problem, Session[U]]

def autoLogoutMillis: Option[Int] = None

def keyPrefix: String = "session-"

def redisPool: RedisClientPool = new RedisClientPool("localhost", 6379)

def read(id: Uuid): FutureValidation[Problem, Session[U]] = {
cache.get(id.toString).toSuccess(Problems.Client.noSession).fv
}

def save(session: Session[U]): FutureValidation[Problem, Session[U]] = {
cache.set(session.id.toString, session)
session.success.fv
}

def delete(id: Uuid): FutureValidation[Problem, Unit] = {
cache.delete(id.toString)
().success.fv
}

object cache extends SimpleRedisCache[Session[U]] {

val pool = redisPool
val prefix = keyPrefix
val ttl = autoLogoutMillis

def create(session: Session[U]): String = {
val sessionId = Uuid.create.toString
set(sessionId, session)
sessionId
}

implicit object valueFormat extends RedisFormat({
case session: Session[U] =>
import blueeyes.json.Printer._
compact(render(sessionFormat.write(session))).getBytes("UTF-8")
})

implicit object valueParse extends RedisParse[Session[U]]({ data: Array[Byte] =>
import blueeyes.json.JsonParser

val session =
for {
json <- JsonParser.parseValidated(new String(data, "UTF-8"))
session <- sessionFormat.read(json)
} yield session

session.fold(
failure = f => sys.error(f.toString),
success = s => s
)
})
}
}
50 changes: 50 additions & 0 deletions redis/src/main/scala/bigtop/util/RedisCache.scala
@@ -0,0 +1,50 @@
package bigtop.util

import com.redis._
import com.redis.serialization.{
Format => RedisFormat,
Parse => RedisParse
}

trait RedisCache[Key, Value] {
def pool: RedisClientPool

def encodeKey(key: Key): String

def ttl: Option[Int]

implicit def valueFormat: RedisFormat
implicit def valueParse: RedisParse[Value]

def set(key: Key, value: Value, ttl: Option[Int] = this.ttl): Unit = {
pool.withClient { redis =>
ttl match {
case Some(ttl) => redis.setex(encodeKey(key), ttl, value)
case None => redis.set(encodeKey(key), value)
}
}
}

def get(key: Key, ttl: Option[Int] = this.ttl): Option[Value] = {
pool.withClient { redis =>
val k = encodeKey(key)
val v = redis.get[Value](k)
ttl.foreach(redis.expire(k, _))
v
}
}

def delete(key: Key): Unit = {
pool.withClient { redis =>
redis.del(encodeKey(key))
}
}
}

trait SimpleRedisCache[Value] extends RedisCache[String, Value] {
def prefix: String

final def encodeKey(key: String): String = {
prefix + key
}
}
6 changes: 3 additions & 3 deletions user/src/main/scala/bigtop/user/LruMapSessionActions.scala
Expand Up @@ -10,9 +10,9 @@ import bigtop.concurrent._
import scalaz.syntax.validation._
import scalaz.std.option.optionSyntax._

case class LruMapSessionActions[U <: User](val userActions: UserActions[U]) extends SessionActions[U]
with FutureImplicits
{
case class LruMapSessionActions[U <: User](
val userActions: UserActions[U]
) extends SessionActions[U] with FutureImplicits {
val map = new SynchronizedLruMap[Uuid, Session[U]](16384)

def read(id: Uuid): SessionValidation = {
Expand Down

0 comments on commit 90f56f4

Please sign in to comment.