Skip to content

Instantly share code, notes, and snippets.

@matthandlersux
Last active December 25, 2015 12:39
Show Gist options
  • Save matthandlersux/6978251 to your computer and use it in GitHub Desktop.
Save matthandlersux/6978251 to your computer and use it in GitHub Desktop.
testing a possible way to serialize to and from a nosql database
object Serializers {
trait Serializable[T] {
def serialize(t: T): String
def fromSerialized(s: String): Option[T]
}
object Serializable {
implicit object SerializableInt extends Serializable[Int] {
def serialize(t: Int) = s"int: $t"
def fromSerialized(s: String): Option[Int] = try {
s.split(": ").tail.map{s => s.toInt}.headOption
} catch {
case e: Exception => None
}
}
implicit object SerializableDouble extends Serializable[Double] {
def serialize(t: Double) = s"double: $t"
def fromSerialized(s: String): Option[Double] = try {
s.split(": ").tail.map{s => s.toDouble}.headOption
} catch {
case e: Exception => None
}
}
}
}
// each database has to extend these
trait AbstractPersistence {
type SerializedType
def toDatabase: SerializedType
def fromDatabase(list: SerializedType): Unit
}
// base trait for models
trait AbstractModel[K] extends AbstractPersistence {
import Serializers.Serializable
val key: K
class Field[T : Serializable](name: String, var value: Option[T] = None) {
Field.add(name, this)
def set(t: T) = value = Some(t)
}
object Field {
implicit def toOpt[T](f: Field[T]): Option[T] = f.value
var map = Map[String, () => Option[String]]()
var setters = Map[String, String => Unit]()
def add[T](name: String, field: Field[T])(implicit stringer: Serializable[T]): Unit = {
val toString = () => field map stringer.serialize
val fromString = (s: String) => stringer.fromSerialized(s) foreach { v => field set v }
map = map + (name -> toString)
setters = setters + (name -> fromString)
}
}
}
// an example of how a particular database might extend persistence
trait DatabasePersistence[K] extends AbstractPersistence { self: AbstractModel[K] =>
type SerializedType = Iterable[(K, String, String)]
def fromDatabase(list: SerializedType): Unit = for {
(r, c, v) <- list
} Field.setters.get(c) foreach { setter => setter(v) }
def toDatabase: SerializedType = for {
(k, o) <- Field.map
v <- o()
} yield (key, k, v)
}
// a model can choose it's persistence driver
class Model(val key: String) extends AbstractModel[String]
with DatabasePersistence[String] {
val count = new Field[Int]("count")
val fraction = new Field[Double]("fraction")
}
val m = new Model("123")
m.count set 10
m.fraction set 10.232
// writing to the database
val list = m.toDatabase
val f = new Model("123")
// pulling from the database
f.fromDatabase(list)
assert(f.count.get == m.count.get)
assert(f.fraction.get == m.fraction.get)
@blueskymonster
Copy link

Is there a reason to include the type in the serialized version if you're not reading it when you deserialize? I could understand if you used that to dynamically infer the type when deserializing, but I don't see the point in including if you just ignore what's before the colon when you're deserializing. The only argument for leaving it in I can think of is readability when you're looking directly at the db, but it should be pretty obvious what type of data you're looking at most of the time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment