Last active
December 25, 2015 12:39
-
-
Save matthandlersux/6978251 to your computer and use it in GitHub Desktop.
testing a possible way to serialize to and from a nosql database
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.