What is Salat?
How does it work?
Advanced Usage
Salat is a bi-directional Scala case class serialization library that leverages MongoDB's DBObject (which uses BSON underneath) as its target format.
Salat has dependencies on the latest releases of:
val novusRepo = "Novus Release Repository" at "http://repo.novus.com/releases/"
val novusSnapsRepo = "Novus Snapshots Repository" at "http://repo.novus.com/snapshots/"
val salat = "com.novus" %% "salat-core" % "0.0.7"
import com.novus.salat._
import com.novus.salat.annotations._
import com.novus.salat.global._
case class Alpha(x: String)
scala> val a = Alpha(x = "Hello world")
a: com.novus.salat.test.model.Alpha = Alpha(Hello world)
scala> val dbo = grater[Alpha].asDBObject(a)
dbo: com.mongodb.casbah.Imports.DBObject = { "_typeHint" :
"com.novus.salat.test.model.Alpha" , "x" : "Hello world"}
scala> val a_* = grater[Alpha].asObject(dbo)
a_*: com.novus.salat.test.model.Alpha = Alpha(Hello world)
scala> a == a_*
res0: Boolean = true
A case class instance extends Scala's Product trait, which provides a product
iterator over its elements.
Salat used pickled Scala signatures to turn case classes into indexed fields with
associated type information.
These fields are then serialized or deserialized using the memoized indexed fields
with type information.
For more information about pickled Scala signatures, see
scala.tools.scalap.scalax.rules.scalasig.ScalaSigParser
In addition, refer to this brief paper:
SID # 10 (draft) - Storage of pickled Scala signatures in class files
a Context has global serialization behavior including:
_typeHint
Grater can serialize and deserialize an individual case class
The context is an implicit supplied by importing Salat's global package object
(or your own package object).
import com.novus.salat.global._
Graters are created on first request. Use the grater method supplied in
Salat's top level package object:
import com.novus.salat._
grater[Alpha].asObject(dbo)
For more information on how to write and use BSON encoding hooks, see the Casbah API docs:
Briefly: Automatic Type Conversions
com.mongodb.casbah.commons.conversions.scala.RegisterConversionHelpers()
com.mongodb.casbah.commons.conversions.scala.RegisterJodaTimeConversionHelpers()
SalatDAO makes it simple to start working with your case class objects. Use it
as is or as the basis for your own DAO implementation.
By extending SalatDAO, you can do the following out of box:
import com.novus.salat._
import com.novus.salat.global._
case class Omega(_id: ObjectId = new ObjectId, z: String, y: Boolean)
object OmegaDAO extends SalatDAO[Omega, ObjectId](
collection = MongoConnection()("quick-salat")("omega"))
scala> val o = Omega(z = "something", y = false)
o: com.novus.salat.test.model.Omega = Omega(4dac7b3e75e1b63949139c91,
something,false)
scala> val _id = OmegaDAO.insert(o)
_id: Option[com.mongodb.casbah.Imports.ObjectId] = Some(4dac7b3e75e1b63949139c91)
scala> val o_* = OmegaDAO.findOne(MongoDBObject("z" -> "something"))
o_*: Option[com.novus.salat.test.model.Omega] = Some(Omega(4dac7b3e75e1b63949139c91,
something,false))
scala> val toUpdate = o.copy(z = "something else")
toUpdate: com.novus.salat.test.model.Omega = Omega(4dac7b3e75e1b63949139c91,
something else,false)
scala> OmegaDAO.update(MongoDBObject("z" -> "something"), toUpdate)
com.mongodb.CommandResult = { "updatedExisting" : true , "n" : 1 ,
"connectionId" : 255 , "err" : null , "ok" : 1.0}
scala> val o_** = OmegaDAO.findOneByID(new ObjectId("4dac7b3e75e1b63949139c91"))
o_**: Option[com.novus.salat.test.model.Omega] = Some(Omega(4dac7b3e75e1b63949139c91,
something else,false))
scala> OmegaDAO.remove(updated)
res1: com.mongodb.CommandResult = { "n" : 1 , "connectionId" : 255 ,
"err" : null , "ok" : 1.0}
When using a case class typed to a trait or abstract superclass, mark it with the @Salat annotation:
@Salat
trait Zeta {
val x: String
}
case class Eta(x: String) extends Zeta
case class Iota(z: Zeta)
scala> val i = Iota(z = Eta("eta"))
i: com.novus.salat.test.model.Iota = Iota(Eta(eta))
scala> val dbo = grater[Iota].asDBObject(i)
dbo: com.mongodb.casbah.Imports.DBObject = { "_typeHint" :
"com.novus.salat.test.model.Iota" , "z" :
{ "_typeHint" : "com.novus.salat.test.model.Eta" , "x" : "eta"}}
scala> val i_* = grater[Iota].asObject(dbo)
i_*: com.novus.salat.test.model.Iota = Iota(Eta(eta))
case class Omicron(@Key("_id") id: ObjectId = new ObjectId,
@Key("saluations") x: String)
scala> val o = Omicron(x = "ave")
o: com.novus.salat.test.model.Omicron = Omicron(4dac7fe775e101ed63792313,ave)
scala> val dbo = grater[Omicron].asDBObject(o)
dbo: com.mongodb.casbah.Imports.DBObject = { "_typeHint" : "com.novus.salat.test.model.Omicron" ,
"_id" : { "$oid" : "4dac7fe775e101ed63792313"} , "saluations" : "ave"}
scala> val o_* = grater[Omicron].asObject(dbo)
o_*: com.novus.salat.test.model.Omicron = Omicron(4dac7fe775e101ed63792313,ave)
Values marked with @Persist will be serialized to DBO and then discarded when deserialized back to the case class.
case class Psi(x: String) {
@Persist val reversed = x.reverse
}
scala> val p = Psi(x = "persist me")
p: com.novus.salat.test.model.Psi = Psi(persist me)
scala> p.reversed
res0: String = em tsisrep
scala> val dbo = grater[Psi].asDBObject(p)
dbo: com.mongodb.casbah.Imports.DBObject = { "_typeHint" : "com.novus.salat.test.model.Psi" ,
"x" : "persist me" , "reversed" : "em tsisrep"}
scala> val p_* = grater[Psi].asObject(dbo)
p_*: com.novus.salat.test.model.Psi = Psi(persist me)
Switch to only using type hints "when necessary":
package object when_necessary {
implicit val ctx = new Context {
val name = Some("TestContext-WhenNecessary")
override val typeHintStrategy = TypeHintStrategy(when =
TypeHintFrequency.WhenNecessary, typeHint = TypeHint)
}
}
Or choose a different type hint:
package object custom_type_hint {
val CustomTypeHint = "_t"
implicit val ctx = new Context {
val name = Some("TestContext-Always-Custom-TypeHint")
override val typeHintStrategy = TypeHintStrategy(when = TypeHintFrequency.Always,
typeHint = CustomTypeHint)
}
}
The specs in the Salat source code provide many usage examples.
Github
https://github.com/novus/salat
Quick Start Guide
https://github.com/novus/salat/wiki/Quick-start
Mailing List
http://groups.google.com/group/scala-salat
Twitter
@prasinous
Is your project using Salat? Let us know about it!