Giter Club home page Giter Club logo

salat's Introduction

Salat

Build Status

Salat is a simple serialization library for case classes.

Salat currently supports bidirectional serialization for:

  • MongoDB's DBObject (using casbah)
  • JSON (using JSON4S)
  • maps

Goals

Simplicity. Flexibility. Consistency.

Your model there and back again should just work.

Get Salat

Salat publishes snapshots and releases to OSS Sontatype.

Stable Release

Available for Scala 2.10, 2.11 and 2.12. Based on Casbah 3.1.1, with support for Mongo 3.x

"com.github.salat" %% "salat" % "1.11.2"

Release Notes

Snapshot

Available for Scala 2.11 and 2.12. Based on Casbah 3.1.1, with support for Mongo 3.x

"com.github.salat" %% "salat" % "1.11.3-SNAPSHOT"

SNAPSHOT Release Notes

Legacy support

Package

Starting with version 1.10.0, the package for Salat has changed.

Version 1.10.x and Later

import salat._

Version 1.9.x and Earlier

import com.novus.salat._

Repositories

Salat has been hosted exclusively by Sonatype since version 0.0.8. Please remove all references to repo.novus.com from your build files.

If you are not using sbt 0.11.2+, or you need a SNAPSHOT release, explicitly add OSS Sonatype to your resolvers:

resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

Scala 2.12.x

Based on Casbah 3.1.1.

"org.github.salat" %% "salat" % "1.11.2"

Scala 2.11.x

Based on Casbah 3.1.1.

"org.github.salat" %% "salat" % "1.11.2"

Based on Casbah 2.8.2.

"org.github.salat" %% "salat" % "1.10.0"

Scala 2.10.4

Based on Casbah 2.7.1.

"com.novus" %% "salat" % "1.9.10"

Scala 2.9.3

Based on Casbah 2.7.0.

"com.novus" %% "salat" % "1.9.7"

Scala 2.9.2

Based on Casbah 2.6.4.

"com.novus" %% "salat" % "1.9.5"

Scala 2.8.1

Based on Casbah 2.1.5-1.

"com.novus" %% "salat" % "0.0.8"

Release Notes

Play 2 plugin

Are you using Play framework? Make sure to see our Play support wiki page, and check out the play-salat plugin at cloudinsights/play-salat.

Documentation

See the wiki and the mailing list.

What does Salat support?

See Supported Types.

What doesn't Salat support?

We don't have the resources to support everything. Here are some things Salat doesn't do:

  • Java compatibility
  • non-case classes
  • type aliases
  • nested inner classes
  • varags
  • arrays
  • multiple constructors
  • tuples
  • Option containing a collection (see collection support for workarounds)
  • relationship management like a traditional ORM

How does Salat work?

Salat uses the Product trait implemented by case classes with the hi-fi type information found in pickled Scala signatures.

Details are thin on the ground, but here's where we got started:

salat's People

Contributors

ai-tsvetkov avatar akraievoy avatar analytically avatar danslapman avatar davidillsley avatar dieu avatar edmondop avatar etorreborre avatar gildegoma avatar gkolpin avatar glorat avatar janxspirit avatar jeffsack avatar jvandew avatar manuelbernhardt avatar maxaf avatar noahlz avatar oliverdodd avatar panshin avatar pferrel avatar rktoomey avatar rozza avatar rusabakumov avatar sullis avatar theunforgiven avatar varju avatar waffle-iron avatar wajda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

salat's Issues

Grater has trouble dealing with List-s and Option-s that live next to eachother

This happened to me a few times already: basically, whenever I have a case class with a collection member followed by an Option, I get a grater glitch on wrong number of arguments. For example (simplified):

case class MetadataRecord(
  validOutputFormats: List[String] = List.empty[String], // valid formats this records can be mapped to
  transferIdx: Option[Int] = None, // 0-based index for the transfer order
  deleted: Boolean = false, // if the record has been deleted
)

will not work, whilst

case class MetadataRecord(
  validOutputFormats: List[String] = List.empty[String], // valid formats this records can be mapped to
  deleted: Boolean = false, // if the record has been deleted
  transferIdx: Option[Int] = None, // 0-based index for the transfer order
)

does work.

Strange error with @Ignore

I've experienced a rather strange error with the @ignore statement.

Given the following class:

case class ConcreteClass(@Ignore var file: File = null) extends AbstractClass

Base class

    abstract class AbstractClass(@Key("_id")val id:ObjectId = new ObjectId, var text:String)

What I now get on serialization is the following error:
json can't serialize type : class java.io.File
I stepped through it with the debugger and it seems to ignore the @ignore statement in the class "ConcreteClass".

API improvement suggestion for SalatDAO

SalatDAO offers convenient methods such as update, insert, ...

However, unlike casbah, these methods are Units and do not return a com.mongodb.WriteResult. So this makes checking whether an object has been written sort of awkward. Would it perhaps be possible to return such a nice WriteResult?

Or is there another way to do the checking that I am not aware of?

Request: enable grater to be aware of implicits for constructor parameters

I think I might have stumbled on a bug: my case class looks like this:

case class Dataset(foo: String, ...)(implicit @Ignore val configuration: DomainConfiguration) { .. }

I get a long list, and especially:

---------- CONSTRUCTOR EXPECTS FOR PARAM [19] --------------
NAME:         [Empty]
TYPE:         models.DomainConfiguration
DEFAULT ARG   [Missing, but unnecessary because input value was supplied]
@Ignore       [???]
---------- CONSTRUCTOR INPUT ------------------------
[!!! MISSING - VALUE INPUT TO CONSTRUCTOR WAS ALSO MISSING !!!]
------------------------------------------------------------

I think the @Ignore [???] doesn't look quite right

Make dao.projections and dao.primitiveProjections return cursor instead of list

Currently it is impossible to get sorted / limited / skipped projection. Only two opitons are available:

  • use find() and get full documents which is unacceptable for large documents
  • use projections() and process projections in memory which is unacceptable for large amount of elements

Making projections(...) to return cursor would allow to accomplish this without disadvantages of methods above

Scala 2.10 support

Since scala 2.10-R1 is going to get released in the middle of next week, the development of salat should head there too.

2.10 brings a lot of awesome meta-programming features which in my opinion would help to make salat even more awesome.

BasicDBList cannot be cast to scala.collection.immutable.List

So I have this issue with 0.0.8-snapshot (binary, not source... compiling the current HEAD causes all manner of crazy problems with option, so im assuming its less stable).

I have a simple Map[String, List[String]] and as you can see from the console session below, what it returns is indeed not actually a List, but presumably somewhere inside salat its been cast to one so that it satisfies the type checker, but when trying to access the actual object the whole world explodes in a blazing ball of fire. For the moment its not the end of the world as I can just compress the list into a string as I wont actually be unpacking it again, but i'm assuming this is some kind of issue with Salat.

Cheers, Tim

scala> res0.get.versions
res17: Map[String,List[String]] = Map(302e302e322d534e415053484f54 -> [ "2.9.1.Final" , "2.9.1-RC1" , "2.9.0-1.Final"])

scala> res17.map(_._1)
res18: scala.collection.immutable.Iterable[String] = List(302e302e322d534e415053484f54)

scala> res17.map(_._2)
java.lang.ClassCastException: com.mongodb.BasicDBList cannot be cast to scala.collection.immutable.List
at $anonfun$1.apply(:10)
at $anonfun$1.apply(:10)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
at scala.collection.immutable.Map$Map1.foreach(Map.scala:118)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:194)
at scala.collection.immutable.Map$Map1.map(Map.scala:106)
at .(:10)
at .()
at .(:11)
at .()
at $export()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:592)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$10.apply(IMain.scala:828)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:31)
at java.lang.Thread.run(Thread.java:680)

scala 2.9.1 build

I'd love to be able to use salat with scala 2.9.1!
You're doing a great job, thank you, and keep it up

Possible bug in ToJValue.FromJValue

Hi Rose. I see you very recently made a change in ToJValue, adding these lines:

  case o: JObject if field.tf.isOption && childType.isEmpty => field.typeRefType.typeArgs match {
    case List(childType: TypeRefType) => apply(j, field, Some(childType))
    case notOption                    => sys.error("FromJValue: expected type for Option but instead got:\n%s".format(notOption))
  }

With this code in place I get an exception. Commenting these lines out my code works. (Of course I don't know what else I might break by commenting out these lines... they're probably there for good reason.) Here's my use case:

case class Person(@Key("_id") name: String, age: Int)
case class Relation(husband: Person, wife: Person, years: Int)
object Relation extends ModelCompanion[Relation, ObjectId] {
val collection = MongoConnection()("junk_db")("nothing")
val dao = new SalatDAO[Relation, ObjectId](collection = collection){} 
}

val p2 = Relation(Person("Fred",45),Person("Mary",40),15) 
val pjs2 = Relation.toCompactJson(p2)   // No problem
val pp2 = Relation.fromJSON(pjs2)           // Goes boom.

Here's the exception:

Exception in thread "main" java.lang.NoSuchMethodError: com.novus.salat.TypeFinder.isOption()Z
at com.novus.salat.json.FromJValue$$anonfun$apply$5.gd3$1(ToJValue.scala:122)
at com.novus.salat.json.FromJValue$$anonfun$apply$5.apply(ToJValue.scala:100)
at com.novus.salat.json.FromJValue$$anonfun$apply$5.apply(ToJValue.scala:100)
at scala.Option.map(Option.scala:133)
at com.novus.salat.json.FromJValue$.apply(ToJValue.scala:100)
at com.novus.salat.ConcreteGrater$$anonfun$8.apply(Grater.scala:410)
at com.novus.salat.ConcreteGrater$$anonfun$8.apply(Grater.scala:405)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:233)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:233)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:76)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:233)
at scala.collection.immutable.List.map(List.scala:76)
at com.novus.salat.ConcreteGrater.fromJSON(Grater.scala:405)
at com.novus.salat.ConcreteGrater.fromJSON(Grater.scala:108)
at com.novus.salat.Grater.fromJSON(Grater.scala:73)
at com.novus.salat.dao.ModelCompanion$class.fromJSON(ModelCompanion.scala:121)
at foo.Relation$.fromJSON(Model.scala:47)
at foo.Salat$delayedInit$body.apply(Salat.scala:39)
at scala.Function0$class.apply$mcV$sp(Function0.scala:34)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:60)
at scala.App$$anonfun$main$1.apply(App.scala:60)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:76)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:30)
at scala.App$class.main(App.scala:60)
at foo.Salat$.main(Salat.scala:24)
at foo.Salat.main(Salat.scala)

Serialisation of List(Byte) / Byte

Is there any reason why Byte is serialized to Integer instead of BinData? And If so how can I force that a case class value is stored as BinData?

Request: Optional Collections

If a case class has an Option value that contains a List of embedded case class objects, the result stored in Mongo will be different than if the value was not an Option.

Spec that demonstrates the issue:

import com.mongodb.casbah.Imports._
import com.novus.salat.dao._
import com.novus.salat.global._
import org.specs2.mutable._

case class ChildReq(left: String, right: String)
case class ParentReq(child: List[ChildReq], _id: ObjectId = new ObjectId())
object ParentReqDAO extends SalatDAO[ParentReq, ObjectId](MongoConnection()("test")("Parent"))

case class ChildOpt(left: String, right: String)
case class ParentOpt(child: Option[List[ChildOpt]], _id: ObjectId = new ObjectId())
object ParentOptDAO extends SalatDAO[ParentOpt, ObjectId](MongoConnection()("test")("Parent"))

class PersisterTest extends SpecificationWithJUnit {

  "mandatory lists of embedded values" should {
    "serialize as objects" in {
      val parent = ParentReq(List(ChildReq("l1", "r1")))
      ParentReqDAO.save(parent)

      val loaded = ParentReqDAO.findOneByID(parent._id).get
      parent must be equalTo (parent)

      val raw = ParentReqDAO.collection.findOneByID(parent._id).get
      val child = raw.getAs[BasicDBList]("child").get
      val elem = child.getAs[BasicDBObject]("0").get
      elem.containsField("left") must be equalTo (true)
      elem.containsField("right") must be equalTo (true)
    }
  }

  "optional lists of embedded values" should {
    "serialize as objects" in {
      val parent = ParentOpt(Some(List(ChildOpt("l1", "r1"))))
      ParentOptDAO.save(parent)

      val loaded = ParentOptDAO.findOneByID(parent._id).get
      parent must be equalTo (parent)

      val raw = ParentOptDAO.collection.findOneByID(parent._id).get
      val child = raw.getAs[BasicDBList]("child").get
      val elem = child.getAs[BasicDBObject]("0").get
      elem.containsField("left") must be equalTo (true)
      elem.containsField("right") must be equalTo (true)
    }
  }
}

SalatDAO doesn't honour typeHints when fetching case classes

@Salat
trait User {
  val id: ObjectId
  val email: String
  val password: String
  val salt: String
  val firstName: String
  val lastName: String

  val added = new DateTime(id.getTime)

  def name = firstName + " " + lastName
}

object User extends ModelCompanion[User, ObjectId] {

  def collection = mongoCollection("users")
  val dao = new SalatDAO[User, ObjectId](collection) {}

  // Indexes
  collection.ensureIndex(DBObject("email" -> 1), "user_email", true)
  collection.ensureIndex(DBObject("email" -> 1, "password" -> 1), "user_email_password")

  def findByEmail(email: String): Option[User] = findOne(DBObject("email" -> email))

  def authenticate(email: String, password: String): Option[User] = findOne(DBObject("email" -> email, "password" -> password))
}

I know want to subclass this so that I can have different types of users. say a Admin and Customer

case class Admin(
  id: ObjectId,
  email: String,
  password: String,
  salt: String,
  firstName: String,
  lastName: String
) extends User

object Admin extends ModelCompanion[Admin, ObjectId] {

  def collection = mongoCollection("users")
  val dao = new SalatDAO[Admin, ObjectId](collection) {}

  // Constructors
  def apply(email: String, password: String, firstName: String, lastName: String): Admin = Admin(ObjectId.get, email, password, ObjectId.get.toString/*Using a object id as a salt*/, firstName, lastName)

  // Finders
  def findBySlug(slug: String): Option[Admin] = findOne(DBObject("slug" -> slug))

  def findByEmail(email: String): Option[Admin] = findOne(DBObject("email" -> email))
}

This works. and I can store the different user types to the same collection.
The problem comes when I wan't to query for say only the Admins.

val admins = Admin.findAll().toList

This won't work because this fetches all the users not only the admins.

The functionality I'm after is that the SalatDAO should filter it's collection so it only returns entities of the same type as the DAO

Ps.
I hope you can get it working Rose :)

Classloader issues in Play 2 workaround

As you are most likely aware of the classloader issue in Play 2, I tried using the prescribed salatctx workaround.

I tried the approach used inย https://github.com/andypetrella/classloading-problemย and it messes with the persistence in that it wraps each object. For example, without the implicit salatctx saving a user object gives me the following:

{ "_id" : ObjectId("4fb586b7ccf2ddffeb898b87"), "username" : "aczerwonka", "email" : "[email protected]" }

That's what I expect. When I search by username, I find the right version and everything is great. But, when I import the implicit salatctx, the same operation results in:

{ "_id" : ObjectId("4fb58a3dccf268cf9d43720e"), "_typeHint" : "models.User", "id" : ObjectId("4fb58a2fccf268cf9d43720c"), "username" : "aczerwonka", "email" : "[email protected]" }

This is not what I expect. My findByUsername obviously fails and I end up creating many User objects in the mongo database. I'm not sure what is happening the the implicit salatctx magic is magically messing up everything.

Not enough issues

Just converted a project from Hibernate to Salat and MongoDB; after getting everything to compile, I ran the code... and it worked flawlessly. The only error I found was where I'd forgotten to use Option[String] instead of String in a couple of cases.

I'm used to days, even weeks of set up and configuration, dealing with transaction management issues and figuring out JDBC connection pooling and Hibernate mapping bugs. Having something work directly and immediately is simply unheard of. As this is almost unprecedented in my experience, I'm going to have to file this as a bug. Please over-complicate the API as much as you can, require several unfindable binary jars with dependencies on invalid cross-cutting versions, and write the documentation with reference to your PhD thesis in category theory.

Nested class fails to map

Following code:

trait ModelCtx {
  val model : Model
  case class Employee(name: String, age: Option[Int], salary: Option[BigDecimal])
  class Model {}
}

trait ViewCtx {
  this : ModelCtx =>
  val view : View
  class View {}
}

object TestApp extends ModelCtx with ViewCtx {
  val model = new Model
  val view = new View
}

val cc = TestApp.Employee("Frank", Some(7), Some(BigDecimal("13.2")))
val dbo = grater[TestApp.Employee].asDBObject(cc)

Fails on last statement with

Exact stack trace:
java.lang.Exception: failed to parse pickled Scala signature from class piurko.ModelCtx$Employee
at com.novus.salat.Grater.sym(Grater.scala:47)
at com.novus.salat.Grater.indexedFields(Grater.scala:51)
at com.novus.salat.Grater.asDBObject(Grater.scala:91)

Weird case of ScalaSigParserError: Unexpected failure

I managed to consistenly get the following stacktrace, even after a complete cleaning of the project:

Caused by: scala.tools.scalap.scalax.rules.ScalaSigParserError: Unexpected failure
    at com.novus.salat.ConcreteGrater.typeRefType(Grater.scala:223)
    at com.novus.salat.ConcreteGrater$$anonfun$indexedFields$3.apply(Grater.scala:149)
    at com.novus.salat.ConcreteGrater$$anonfun$indexedFields$3.apply(Grater.scala:146)
    at com.novus.salat.ConcreteGrater.indexedFields(Grater.scala:146)
    at com.novus.salat.ConcreteGrater.iterateOut(Grater.scala:228)
    at com.novus.salat.ConcreteGrater.asDBObject(Grater.scala:244)
    at com.novus.salat.ConcreteGrater.asDBObject(Grater.scala:53)
    at com.novus.salat.dao.SalatDAO.insert(SalatDAO.scala:197)
    at com.novus.salat.dao.SalatDAO.insert(SalatDAO.scala:193)
    at controllers.organization.DataSetControl$.dataSetSubmit(DataSetControl.scala:55)

By simply adding a String field to a case class that would otherwise work like a charm. After some debugging I finally figured out that the culprit was the following member:

indexingMappings: List[String] = List.empty[String]

After renaming it to idxMappings things started working again. Is this maybe some kind of reserved keyword somewhere? I have trouble thinking of another explanation.

I'm not quite sure if this issue report is useful, I thought I'd just send it in case it could help somebody.

JSON parsed into DBObject using c.m.u.JSON.parse always transposes ints to longs

After upgrading to mongo-java-driver 2.5.2 (from 2.4), behavior of JSON.parse has changed. Where it used to dumbly parse all bare numeric values into instances of Int, it now (correctly) parses them as Long-s.

Salat has enough destination type info available to it in order to correctly translate these Long-s into Int, Double, BigDecimal, or whatever else is expected by the case class constructor.

Failing spec: com.novus.salat.test.MongoJavaDriver252IntLongBugSpec

Manifestation of this in real world:

ERR [20110327-10:56:03.719] [qtp751766815-41] c.n.a.w.Session$ oops
com.novus.salat.ToObjectGlitch: 

  argument type mismatch

  Grater(class com.novus.analytics.model.auth.ClientAware @ com.novus.salat.global.package$$anon$1@203a16) toObject failed on:
  SYM: com.novus.analytics.model.auth.ClientAware
  CONSTRUCTOR: public     com.novus.analytics.model.auth.ClientAware(org.bson.types.ObjectId,int,scala.Option,scala.Option)
  ARGS:
  [0]   class org.bson.types.ObjectId
        4d8f4fe326c4aebc1f948166
[1] class java.lang.Long
        1
[2] class scala.Some
        Some(Novus Partners, Inc.)
[3] class scala.Some
        Some(NV1)

I'm trying to run this on Windows

Can you send teh codez? I installed SBT but it complains Salat couldn't be found.

PS: I'm running Windows XP SP1 on a Eee PC netbook.

Many thanks.

slf binding issue

Hi,

Can you use slf4j-api instead of slf4j-simple? I have a project which uses slf4j-classic and this causes a warning and results in slf4j binding to the first implementation is finds.

slf4j is also upto 1.6.4 if you wanted to update while you're in that part of the code.

Thanks

Robbie

Request: Custom Serialization for Specific Types

I want to have a List of objects (w/ ID) being serialized by only serialize its ID which is an ObjectId.

If it's not possible now.
I saw that in Grater#outField it would be possible to add a kind of customSerializer call with the resulting value for the field.

So we could have either

  • an extra parameter that is a optional CustomSerializer (Any to Any)
  • an extra Salat context variable that defines mapping between Class#field and serializer

Such serializer would be called right before the "result" of outField

Map with Enum keys serializes to JSON but fails with ClassCastException on deserialization

Referencing this sample:

object Scope extends Enumeration {
    val ONE, TWO, THREE = Value
}

case class Blather( name:String, scope:Map[String,Scope.Value], other:Scope.Value)

object Salat extends App {  
    val b = Blather("Greg", Map("a"->Scope.TWO), Scope.TWO)
    val js = grater[Blather].toCompactJSON(b)
    val b2 = grater[Blather].fromJSON(js)
    println(b2)
    val z = b2.scope.get("a")
    println(z)
    println(z.get.getClass) // Eek!  This is now a Map{String,String]...lost Scope.Val
    println(b2.other.getClass) // This is ok...it's a Enumeration$Val
    val y = z.flatMap( b => Some(true) )  // This goes boom w/ClassCastException
}

I'm getting a ClassCastException due to losing type information upon json deserialization. Enums are deserializing fine by themselves but when I have a Map[String,Enum] I have a problem. They serialize to json just fine, but go boom when trying to deserialize. When I print out the value and poke around to see its class I can see that it thinks the value of the Map is a String not an Enum val as expected.

Cannot deserialize JSON to case class with Optional enum field.

Hi Rose,

I ran into a error when deserializing a JSON string to a model that has enums. If I do not overwrite the enum strategy I get the below error

  [error] Not sure how to handle value='Some(mon)' as enum of class model.Weekday using strategy BY_VALUE

and if I overwrite the enum strategy to BY_ID I get the below error.

   [error] Not sure how to handle value='Some(1)' as enum of class model.Weekday using strategy BY_ID

I have updated my existing MongoDate github project that recreates these errors. To run the test ,get the latest and use the below commands

test-only -- include testEnum

The github project is at https://github.com/mzafer/MongoDate

Thanks
Zafer

Support for traits in serialization

Given this model:

trait FooInterface

case class Bar(foo: FooInterface)

case class FooA(a: String) extends FooInterface

case class FooB(b: String) extends FooInterface

When I serialize a Bar instance, it results to:

{ "_typeHint" : "com.novus.salat.test.model.Bar" , "foo" : [ "text"]}

There is no indication whether the inner object is a FooA or a FooB instance.
So the deserialization fails.

Should this case be supported?

Request: Ignore @Key for JSON

Hello,

I love the new bi-direction JSON support! Solid stuff and extremely useful.

One change would be very cool, though. The current code utilizes a lot of the same code used for DBObjects, specifically, the @key annotation replaces a field name with what you specify (typically "_id" for Mongo).

This is fantastic for Mongo but it's not a good thing for JSON generally. For example if you're getting JSON to send to the UI you'd want all the normal field names, not have one labled _id because Mongo needed it that way.

So the idea is to have JSON marshalling ignore the @key field name resolution and just use the normal field name to/from JSON.

Clearly this touches a few areas in the code. I have a working sample here: https://github.com/gzoller/salat
If you want this implementation let me know and I'll do a pull request.

Comments welcome.
Greg

Request: deserialize JSON objects to Map[String,(CaseClass)]

Salat currently has explicit support for arrays with the toJSONArray/fromJSONArray, but it would be nice if it could handle collections seamlessly. Consider the simple case:

case class Foo(bar: String)

And the data:

{ "qux": { "bar": "Qux" }, "ged": { "bar": "Ged" }, "mog": { "bar": "Mog" } }

It should be possible to deserialise this using the standard grater syntax:

val foos = grater[Map[String, Foo]].fromJSON(json)

Note that I've used JSON in the examples, but this should of course extend to Mongo as well.

Migrate from slf4j to logback

In order to provide trace logging that would allow a Salat user to better debug what on earth is going on with graters, primarily:

  • what was the incoming class?
  • to which CaseClass was it resolved?
  • was a grater found? if not, why not?

Consistency, improvement suggestion

ModelCompanion uses i.e. findOneByID(), while at the same time i.e. removeById() ...parentId(), etc... Types generally follow "Id"-naming.

Changing/deprecating findOneByID (to break with Casbah's naming flavor) is most likely the easiest refactor.

I could do a pull request either direction, but I guess this consistency resolution should probably follow your taste preferences rather than mine.

\o

Please start increasing version number when publishing to repository

I have trouble using SalatDAO as there have been changes in the API without any version change.

I get this error when publishing to Heroku:
[error] /tmp/build_9mef5k0gh217/app/models/Article.scala:29: value findOneById is not a member of com.novus.salat.dao.SalatDAO[models.Article,String]
[error] def get(friendlyUrl:String) = dao.findOneById(friendlyUrl)
[error] ^

I fixed my local version previously by purging my cache, but I can't do that on Heroku afik. It seems like Heroku caches packages. So I'm in a catch 22 situation: If I revert to the old API it works on Heroku but then it won't work locally and vice versa.

Feature request for to add event hooks for persistence events

Between ModelCompanions and SalatDAOs, Salat does a great job of facilitating a development style where API consumers do not need to write much (if any) code to perform CRUD operations with Mongo.

I am pretty new to Salat and Scala, and the Play Framework application I have inherited uses lots of immutable case classes. Since I am limited in performing any validation on the set events of my domain object properties (since they are vals), it would be nice to write annotated functions that fire before/after persistence events and could perform custom validation and throw exceptions to prevent the DAOs from proceeding:

@BeforeSave
def myValidation() = {
    //check something prior to saving 
    //throw exception if necessary
 }  

I am looking for something like Active Record callbacks in Rails or JSR 303 bean validation in Hibernate/JPA.

Thanks again for finding my question on StackOverflow.

Keith

SalatMongoCursor.toList blows up

A sample test program is here. It closely follows the SalatDAO tutorial.

Exception in thread "main" java.util.NoSuchElementException: None.get
    at scala.None$.get(Option.scala:274)
    at scala.None$.get(Option.scala:272)
    at com.novus.salat.ConcreteGrater$$anonfun$6.apply(Grater.scala:350)
    at com.novus.salat.ConcreteGrater$$anonfun$6.apply(Grater.scala:350)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:194)
    at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
    at scala.collection.immutable.List.foreach(List.scala:45)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:194)
    at scala.collection.immutable.List.map(List.scala:45)
    at com.novus.salat.ConcreteGrater.asObject(Grater.scala:350)
    at com.novus.salat.ConcreteGrater.asObject(Grater.scala:75)
    at com.novus.salat.dao.SalatMongoCursorBase$class.next(SalatMongoCursor.scala:43)
    at com.novus.salat.dao.SalatMongoCursor.next(SalatMongoCursor.scala:145)
    at scala.collection.Iterator$class.foreach(Iterator.scala:660)
    at com.novus.salat.dao.SalatMongoCursor.foreach(SalatMongoCursor.scala:145)
    at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48)
    at scala.collection.mutable.ListBuffer.$plus$plus$eq(ListBuffer.scala:128)
    at scala.collection.TraversableOnce$class.toList(TraversableOnce.scala:242)
    at com.novus.salat.dao.SalatMongoCursor.toList(SalatMongoCursor.scala:145)
    at SalatDoodles.<init>(SalatDoodles.scala:42)
    at Main$delayedInit$body.apply(Main.scala:11)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:34)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:60)
    at scala.App$$anonfun$main$1.apply(App.scala:60)
    at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
    at scala.collection.immutable.List.foreach(List.scala:45)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:30)
    at scala.App$class.main(App.scala:60)
    at Main$.main(Main.scala:5)
    at Main.main(Main.scala)

The same sample program also shows how another error can be produced:

error: not enough arguments for method grater: (implicit ctx: com.novus.salat.Context, implicit m: Manifest[Y])
com.novus.salat.Grater[Y]. Unspecified value parameter m.

NoSuchMethodError: com.mongodb.casbah.MongoCollectionBase.find

Howdy!

I'm trying to use SalatDAO with Play! framework 2.0-RC2. But I cannot get the SalatDAO.find function to work:

play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[NoSuchMethodError: com.mongodb.casbah.MongoCollectionBase.find(Ljava/lang/Object;Ljava/lang/Object;Lscala/Function1;Lscala/Function1;)Ljava/lang/Object;]]
        at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:65) [play_2.9.1.jar:2.0-RC2]
        at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:46) [play_2.9.1.jar:2.0-RC2]
        at akka.actor.Actor$class.apply(Actor.scala:292) [akka-actor.jar:2.0-RC1]
        at play.core.ActionInvoker.apply(Invoker.scala:44) [play_2.9.1.jar:2.0-RC2]
        at akka.actor.ActorCell.invoke(ActorCell.scala:481) [akka-actor.jar:2.0-RC1]
        at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:182) [akka-actor.jar:2.0-RC1]
Caused by: java.lang.NoSuchMethodError: com.mongodb.casbah.MongoCollectionBase.find(Ljava/lang/Object;Ljava/lang/Object;Lscala/Function1;Lscala/Function1;)Ljava/lang/Object;
        at com.novus.salat.dao.SalatDAO.find(SalatDAO.scala:321) ~[salat-core_2.9.1-0.0.8-SNAPSHOT.jar:0.0.8-SNAPSHOT]
        at com.novus.salat.dao.SalatDAO.find(SalatDAO.scala:323) ~[salat-core_2.9.1-0.0.8-SNAPSHOT.jar:0.0.8-SNAPSHOT]
        at controllers.Application$$anonfun$doStuff$1.apply(Application.scala:104) ~[classes/:2.0-RC2]
        at controllers.Application$$anonfun$doStuff$1.apply(Application.scala:94) ~[classes/:2.0-RC2]
        at play.api.mvc.Action$$anonfun$apply$4.apply(Action.scala:204) ~[play_2.9.1.jar:2.0-RC2]
        at play.api.mvc.Action$$anonfun$apply$4.apply(Action.scala:204) ~[play_2.9.1.jar:2.0-RC2]

Code is the same as in https://github.com/novus/salat/wiki/SalatDAO:

package dao

import com.novus.salat._
import com.novus.salat.global._
import com.novus.salat.annotations._
import com.novus.salat.dao._
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.MongoConnection

case class Alpha(@Key("_id") id: Int, x: String)
object AlphaDAO extends SalatDAO[Alpha, Int](collection = MongoConnection()("salat")("test_alpha"))

Inserting / findOnById works, but not the "general query".

Is this a bug, or some issue of Salat/Play! combination?

Thanks! :)

Ability to create contexts that can resolve classes across multiple classloaders

Salat currently is hard-coded to use boot time classloader.

To make Salat work with web frameworks, it is necessary to be able to resolve classes from custom classloaders.

First cut at

Context now comes pre-populated with its own context. This method prepends custom classloaders to the existing list:

ctx.registerClassLoader(someCustomClassLoader)

And the getClassNamed method in top-level package object looks for an implicit list of classloaders and attempts to resolve in sequence.

Persist lazy vals on serialize, throw them away during deserialize

Use case: Salat only serializes items in the case class constructor at present. Lazy vals are brilliant because now you can just toss calculations into the case class body without having to fuss about internal business logic dependencies, plus you put off calculation until it's actually necessary.

BUT... if it's a lazy val in the body, it doesn't get serialized. So you can't sort by it! And Mongo is all about being able to sort on the server side.

On top of that, when Mongo does sort on the server side, they don't provide any coalesce or nvl functionality, so if you sort descending, all the nulls go to the top. (WTF? What business user in the history of time demanded to see nulls above the highest values? Discuss.)

So, what I want is the following: declare a lazy val based on the case class constructor inputs, serialize to Mongo and save it so I can sort on it (specifying an optional default value if the supplied val is null or blank or None), and then throw it away and forget about it when I deserialize.

Proposed solution:

case class Foo(x:  Option[BigDecimal], y: Option[BigDecimal]) {
  @Persist @Coalesce lazy val value: Option[BigDecimal] = {
// some tedious business logic involving x and y
  }
}

"the Cheeseburger pattern" - because I want to have my cake and eat it too!

unable to use references to other case classes

Trying to use IMap[Integer,UrlID] results in an error, UrlID is also a case class;
you can see that the field "urls" is a map (basicdbobject), but it is not being converted to IMap[Integer,UrlID].
I've attached my test code and the output of it below.
TestCODE:------------------------------

import com.novus.salat._
import com.novus.salat.global._
import com.mongodb.casbah.Imports._
import scala.collection.immutable.{Map => IMap}
import scala.collection.mutable.{Map => MMap}

case class AttributeObject(_id:Long,key:String,bestDef:String,urls : IMap[Integer,UrlID])
case class UrlID(dh:Long,ph:Long);

class MongoTest {

}
object MongoTest
{
def main(args: Array[String]) {
val mongoConn = MongoConnection("localhost",22222);
val mongoDB = mongoConn("objects")
val mongoCol = mongoDB("preQueryObjects2702");
val testobj = mongoCol.findOne();
val attrObject = grater[AttributeObject].asObject(testobj.get);

    println(attrObject.urls);
    attrObject.urls .values.foreach({x:UrlID => print(x.dh)});

}

}

}

OUTPUT---------------
Map(1053939663 -> { "dh" : 2374 , "ph" : -8004904788607604459}, 616874274 -> { "dh" : 1748507 , "ph" : -1020451305109387027}, 1284102941 -> { "dh" : 3206 , "ph" : 2472657702604043830}, 984049062 -> { "dh" : 3206 , "ph" : -8480890755275550879}, -1953052290 -> { "dh" : 2374 , "ph" : -5966550626876314734}, 991658601 -> { "dh" : 248675 , "ph" : -8480890755275550879}, -977809458 -> { "dh" : 53301 , "ph" : -6298442539866654141}, -1689446304 -> { "dh" : 404062 , "ph" : 2935931490668868895})
Exception in thread "main" java.lang.ClassCastException: com.mongodb.BasicDBObject cannot be cast to UrlID
at MongoTest$$anonfun$main$1.apply(MongoTest.scala:39)
at scala.collection.MapLike$DefaultValuesIterable$$anonfun$foreach$4.apply(MapLike.scala:201)
at scala.collection.MapLike$DefaultValuesIterable$$anonfun$foreach$4.apply(MapLike.scala:201)
at scala.collection.immutable.HashMap$HashMap1.foreach(HashMap.scala:125)
at scala.collection.immutable.HashMap$HashTrieMap.foreach(HashMap.scala:344)
at scala.collection.MapLike$DefaultValuesIterable.foreach(MapLike.scala:201)
at MongoTest$.main(MongoTest.scala:39)
at MongoTest.main(MongoTest.scala)

ClassCastExceptions using Play 2.0

Hi,

we're constantly running into class cast exceptions when using hierarchies with Salat and Play 2.0.

The code looks like this:

@Salat
sealed abstract class EventLike(
  @Persist val _id: ObjectId
  //...
)

case class Event(override val _id: ObjectId = new ObjectId, ...) extends EventLike(_id = _id)
case class MiniEvent(override val _id: ObjectId = new ObjectId, ...) extends EventLike(_id = _id)

The error is:

[ClassCastException: models.Event cannot be cast to models.EventLike]

We're using a custom context, defined in a package object:

package object db {
  implicit val ctx = {
    import com.novus.salat._
    import play.api.Play
    import play.api.Play.current
    val c = new Context {
      val name = "play-salat-context"
    }
    c.registerClassLoader(Play.current.classloader)
    c
  }
}

This happens during development after making some changes, but can't be reproduced reliably.

Insertion does not work on SalatDAO

Everything was working great on my system 24 hours ago... now I'm getting this exception which seems to be related to the latest change:

com.novus.salat.dao.SalatInsertError: SalatDAO[Ticket,ObjectId](tickets): insert failed! [info] [info] Collection: tickets [info] WriteConcern: WriteConcern { "getlasterror" : 1 , "w" : 1 , "wtimeout" : 0} [info] WriteResult: { "n" : 0 , "connectionId" : 8 , "wtime" : 0 , "err" : null , "ok" : 1.0} [info] [info] FAILED TO INSERT DBO [info] { "_typeHint" : "com.ign.topaz.domain.Ticket" , "userId" : "testDao" , "hash" : "8101cfdf444add51f4031f3b34591a50a7f6265f" , "created" : { "$date" : "2012-01-11T23:04:31Z"} , "expires" : { "$date" : "2012-01-10T23:04:31Z"} , "active" : true , "_id" : { "$oid" : "4f0e157f0364bf8c90dcf409"}} [info] at com.novus.salat.dao.SalatDAO.insert(SalatDAO.scala:206)

Inserts are not working at all :-( Please HELP!

Play 20 integration : Reload Class Loader will fail with class caching ?

Hi I'm trying to figure out where comes the problem with Play20 and Salat. Here is a ticket I've create on the related tickket tracker.
https://play.lighthouseapp.com/projects/82401/tickets/216-play-scala-20-recompilation-problem-using-salat#ticket-216-2

The problem is that Play20 recompiles everytime a changes occurs, and the class loader might changes and maybe classes as well (not sure). So, when recompilation occurs a mismatch happens between classes using Grater (it seems)

If you can have a look or give me some hints it'd be very kind.

thanks

andy

Publish Salat scaladocs on github pages.

Currently the scaladoc looks non-empty, so it's better than nothing. (Even scaladoc without additional comments serves some purpose.)

Please put the generated scaladoc online in gh-pages. You can make some use of xsbt-ghpages-plugin if it feels convenient. (I've tried it on my fork, and for now with no success. Currently I have to stop and work on other things.)

Also it would be great to change the link in the short project description on the GitHub page form self-reference to http://novus.github.com/salat

release 0.0.8

Its been a year and we have new versions of Scala, any chance 0.0.8 might actually get released? Even an RC?

Relying on a snapshot is just not possible for some of us (maven release prohibits it for instance).

SalatDAO should support polymorphic collections

Hi,

I'm quite new to MongoDB and Salat, but as far as I understand, I cannot use polymorphic collections (I mean collections typed with an abstract supertype) with the SalatDAO class. So the following currently code throws a compiler error:

case class User(_id: ObjectId = new ObjectId, email: String, password: String, roles: Seq[Role] = Nil)

@Salat
sealed abstract class Role(user: ObjectId)
case class AdminRole(user: ObjectId) extends Role(user)
case class EditorRole(user: ObjectId) extends Role(user)

class UserDAO extends SalatDAO[User, ObjectId](collection = DB.connection("users") {
  // Doesn't work because of type constraints
  val roles = new ChildCollection[Role, ObjectId](collection = DB.connection("roles"), parentIdField = "user") {}
}

Is there a way around this or do you plan to support polymorphic hierarchies in SalatDAO?

Thanks,

  • Marius

.find method does not support 2 search string.

MongoDb Statement:

db.users.find( { age: { $gt: 25, $lt: 50 } } }

This does not support in scala. Here are the statement:

UserModel.find(ref = MongoDBObject("age" -> {
MongoDBObject("$gt" -> 25)
MongoDBObject("$lt" -> 50)
}))

No sure it is my statement wrong. Please enlighten me.

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.