play slick updating enumeration column - scala

I'm having trouble figuring out how to update a column with type enumeration using play-slick.
Here's my enum and case class:
object TestStatus extends Enumeration {
type TestStatus = Value
val Status1 = Value("Status1")
}
case class Test (
id: String,
status: TestStatus
)
and the table mapping:
class Tests(tag: Tag) extends Table[Test](tag, "tests") {
implicit val statusColumn = MappedColumnType.base[TestStatus, String](_.toString, TestStatus.withName)
override def * = (id, status) <> ((Test.apply _).tupled, Test.unapply)
val id = column[String]("id", 0.PrimaryKey)
val status = column[TestStatus]("status")
}
when I try to go and update a Tests row, I get an error:
object TestQueries extends TableQuery[Tests](new Tests(_)) {
def updateStatus(id: String, newStatus: TestStatus) = {
TestQueries.filter(_.id === id).map(_.status).update(newStatus)
}
}
[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection,
[error] you use an unsupported type in a Query (e.g. scala List),
[error] or you forgot to import a driver api into scope.
[error] Required level: slick.lifted.FlatShapeLevel
[error] Source type: slick.lifted.Rep[models.TestStatus.Value]
[error] Unpacked type: T
[error] Packed type: G
[error] TestQueries.filter(_.id === id).map(_.status).update(newStatus)
[error] ^
IntelliJ is showing that TestQueries.filter(_.id === id).map(_.status) has type Query[Nothing, Nothing, Seq], which makes me think the problem is with the specific column rather than with the update function.
Updating the id works fine using the same structure.

You need to define the custom column type of TestStatus.Value. This is how slick allows you to build a custom column type by mapping it to an already supported type:
implicit def testStatCT: BaseTypedType[TestStatus.Value] =
MappedColumnType.base[TestStatus.Value, String](
enum => enum.toString, str => TestStatus.withName(str)
)
this implicit needs to be imported wherever implicit resolutions such as the one in your example fail (or better yet defined in the TestStatus object so that it's always available) this way slick can have evidence that TestStatus.Value is a BaseTypedType which basically just means that something is a supported column type.
For further read on column mapping you can look at Slick Documentation

Related

Not sure how to deserialise and serialise a type which can change

Say I have a model like this:
case class Items(typeOfItems: TypeOfItems)
object Items{
implicit val format = Json.format[Items]
}
sealed trait TypeOfItems extends Product with Serializable
object TypeOfItems {
final case class Toy(typeOfItem : String, name : String, price : Int) extends TypeOfItems
final case class Car(typeOfItem : String, model: String, price : Int ) extends TypeOfItems
}
I cannot do any serialising or deserialising. I get the following errors:
No instance of play.api.libs.json.Format is available for model.TypeOfItems in the implicit scope (Hint: if declared in the same file, make sure it's declared before)
[error] implicit val format = Json.format[Items]
[error] ^
[error] No Json serializer found for type model.Items. Try to implement an implicit Writes or Format for this type.
[error] Ok(Json.toJson(Items(Toy("Toy", "Doll", 2))))
I am not sure how to specify the formats for TypeOfItems. How do I do that?
In Play 2.7 sealed traits are supported in play-json.
As already mentioned in comments all you are missing is:
object TypeOfItems{
implicit val format = Json.format[TypeOfItems]
}
See here the requirements in the documentation: ScalaJsonAutomated
For Play 2.6 see this answer: Noise free JSON format for sealed traits with Play 2.2 library (as the title suggests there are also solutions for older versions)

Parse Play 2 json form errors using Prickle in Scala.js

I'm trying to convert JSON with Play 2 form errors to case classes in Scala.js.
Here is result from server:
{"obj.comment":[{"msg":["error.minLength"],"args":[10]}],"obj.name":[{"msg":["error.path.missing"],"args":[]}]}
Here is my code from Scala.js:
import prickle._
sealed trait Result
case class fmData(name: String, comment: String)
case class MsgData(msg: Array[String], args: Array[Int]) extends Result
case class MyResult(`obj.comment`: Array[MsgData], `obj.name`: Array[MsgData]) extends Result
implicit val resultPickler: PicklerPair[Result] = CompositePickler[Result].concreteType[MsgData].concreteType[MyResult]
just trying to do everything like in the example from https://github.com/benhutchison/prickle
But I got error on compilation stage with error:
could not find implicit value for parameter p:
prickle.Pickler[MsgData] [error] implicit val resultPickler:
PicklerPair[Result] =
CompositePickler[Result].concreteType[MsgData].concreteType[MyResult]
[error]
^
Can't understand why I need to provide Pickler for MsgData. There is no Pickler for concreteType in the example.
How this issue can be fixed?

why does the slick can not store Option[String]?

I use slick 2.0 rc1 in my playframework 2.2 project
my talbe code is:
case class Resource(id: Option[Long] = None, owner: UserId, types: String)
// The static object that does the actual work - note the names of tables and fields in H2 are case sensitive and must be all caps
object Resources extends Table[Resource]( "RESOURCE") {
def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
def owner = column[UserId]("Owner")
def types = column[String]("Type")
def withuser = foreignKey("User_FK", owner, Users)(_.id)
// Every table needs a * projection with the same type as the table's type parameter
def * = id.? ~ owner ~ types <> (Resource, Resource.unapply _)
}
the output error is:
[info] Compiling 16 Scala sources and 2 Java sources to C:\assigment\slick-advan
ced\target\scala-2.10\classes...
[error] C:\assigment\slick-advanced\app\models\Resource.scala:12: overloaded met
hod constructor Table with alternatives:
[error] (_tableTag: scala.slick.lifted.Tag,_tableName: String)play.api.db.slic
k.Config.driver.Table[models.Resource] <and>
[error] (_tableTag: scala.slick.lifted.Tag,_schemaName: Option[String],_tableN
ame: String)play.api.db.slick.Config.driver.Table[models.Resource]
[error] cannot be applied to (String)
[error] object Resources extends Table[Resource]( "RESOURCE") {
[error] ^
I know on document it like:
object Resources(tag: Tag) extends TableTable[(Long, UserId, String)](tag, "RESOURCE") {
but after I change it, it still have error:
[error] C:\assigment\slick-advanced\app\models\Resource.scala:12: traits or obje
cts may not have parameters
[error] object Resources(tag: Tag) extends TableTable[(Long, UserId, String)](ta
g, "RESOURCE") {
[error] ^
[error] one error found
Check out the Scaladoc for Table for the constructors:
new Table(_tableTag: Tag, _tableName: String)
new Table(_tableTag: Tag, _schemaName: Option[String], _tableName: String)
The compiler is telling you that you have those choices but are using neither. You construct your instance of Table called Resources with just a String. As you can see, that isn't an option. No pun intended.
The documentation shows that your declaration should look more like this:
class Resources(tag: Tag) extends Table[(Long, UserId, String)](tag, "RESOURCE") {
...
}
val Resources = TableQuery[Resources]

SORM Persisted 'id' undefined

I am just trying to learn SORM and am playing with what I think is some simple sample code. To whit:
case class Book(var author:String, var title:String);
object Db extends Instance(
entities = Set(Entity[Book]()),
url = "jdbc:h2:mem:test",
user = "",
password = "",
initMode = InitMode.Create
)
And then:
val b : Book with Persisted = Db.save( Book("foo","bar") )
When attempting to compile this, I get:
[error] /Users/rjf/IdeaProjects/PlayTest/app/controllers/Application.scala:22: type mismatch;
[error] found : models.Book
[error] required: sorm.Persisted with models.Book
[error] val b : Book with Persisted = Db.save( Book("foo","bar") )
[error] ^
If I change the Book declaration to:
case class Book(var author:String, var title:String) extends Persisted;
I then get:
[error] /Users/rjf/IdeaProjects/PlayTest/app/models/Book.scala:17: class Book needs to be abstract, since:
[error] it has 2 unimplemented members.
[error] /** As seen from class Book, the missing signatures are as follows.
[error] * For convenience, these are usable as stub implementations.
[error] */
[error] def id: Long = ???
[error] def mixoutPersisted[T]: (Long, T) = ???
[error] case class Book(var author:String, var title:String) extends Persisted;
[error] ^
Apologies for the newbie question. No doubt that the fact I'm learning Scala at the same time is a contributing factor.
To solve your issue, just remove the explicit type annotation:
val b = Db.save( Book("foo","bar") )
Don't worry, you'll still be able to access the same interface as Book with Persisted.
As to why this happens, this seems to be a bug of Scala, and it's been reported.
A sidenote
Don't use vars in case class declaration. Immutability is the whole point of case classes. The correct declaration would be:
case class Book(author:String, title:String)
which is the same as
case class Book(val author:String, val title:String)

Specifying a return type for Slick's MappedProjection

I'm using the PlayFramework 2.1.1 in combination with Slick 1.0.1 and the Play-Slick-Plugin 0.3.2.
Defining an abstract class that enforces my models to implement a "forInsert"-Mapper fails because I'm unable to specify the proper return type. My current definition results in the compile error below but I'm simply unable to track down this issue and provide the correct type.
import play.api.db.slick.Config.driver.KeysInsertInvoker
abstract class Model[M]( val table: String ) extends Table[M]( table )
{
def id = column[Int]( "id", O.PrimaryKey, O.AutoInc )
def forInsert: KeysInsertInvoker[M, Int]
}
object Course extends Model[Course]( "course" )
{
...
def forInsert = name ~ room <> ( apply _, unapply _ ) returning id
}
[error] Course.scala:27: polymorphic expression cannot be instantiated to expected type;
[error] found : [RU]play.api.db.slick.Config.driver.KeysInsertInvoker[model.Course,RU]
[error] required: play.api.db.slick.Config.driver.KeysInsertInvoker[model.Course,Int]
[error] def forInsert = name ~ room <> ( apply _, unapply _ ) returning id
[error] ^
[error] one error found
[error] (sample/compile:compile) Compilation failed
[error] Total time: 3 s, completed 18.06.2013 03:38:24
abstract class Model[M]( val table: String ) extends Table[M]( table )
{
def id = column[Int]( "id", O.PrimaryKey, O.AutoInc )
def forInsert: scala.slick.driver.BasicInvokerComponent#KeysInsertInvoker[M, Int]
}
Not that difficult. Printing a getClass from the implementation solved the mystery quite easily. An idea I didn't come up with yesterday night.