how to reuse an anorm parser in playframework 2.0 with scala
Asked Answered
R

1

6

I've been having a look at the computer-database sample and I noticed that in order to reuse the Computer parser, the list method uses the Computer.withCompany parser, which returns a tuple of (Computer, Company)

In the case I have to handle, instead of a reference to the id of the computer I want to have a Computer object, like this

case class Computer(id: Pk[Long] = NotAssigned, name: String, introduced: Option[Date], discontinued: Option[Date], company: Company)

so I was thinking how can I achieve something like the following (it's seudocode, of course)

val simple = {
  get[Pk[Long]]("computer.id") ~
  get[String]("computer.name") ~
  get[Option[Date]]("computer.introduced") ~
  get[Option[Date]]("computer.discontinued") ~
  get[Company]("company.*") map {
    case id~name~introduced~discontinued~company => Computer(id, name, introduced, discontinued, company)
  }
}

Obviously, the tricky part would be how to solve getCompany

any idea???

Ruphina answered 2/10, 2012 at 1:48 Comment(0)
R
5

I have an Idea entity and an IdeaType entity (it's like Computer and Company, in the computer-database example)

case class IdeaTest(
  val id: Pk[Long]          = NotAssigned,
  val name: String          = "unknown idea",
  val description: String   = "no description",
  val kind: IdeaType        = IdeaType()
)

case class IdeaType (
  val id: Pk[Long] = NotAssigned,
  val name: String = "unknown idea type",
  val description: String = "no description"
)

I define a TypeParser

val typeParser: RowParser[IdeaType] = {
  get[Pk[Long]]("idea_type.id") ~
  get[String]("idea_type.name") ~
  get[String]("idea_type.description") map {
    case id~name~description => IdeaType(
      id, name, description
    )
  }
}

And the first thing I tried was:

val ideaParser: RowParser[IdeaTest] = {
  get[Pk[Long]]("idea.id") ~
  get[String]("idea.name") ~
  get[String]("idea.description") ~
  typeParser map {
    case id~name~description~ideaType => IdeaTest(
      id, name, description, ideaType
    )
  }
}

Even though it DOES compile ok, it always has trouble loading the ideaType.

In the end, I had to define an ideaParser with no ideaType and compose it with a typeParser:

val typeParser: RowParser[IdeaType] = {
  get[Pk[Long]]("idea_type.id") ~
  get[String]("idea_type.name") ~
  get[String]("idea_type.description") map {
    case id~name~description => IdeaType(
      id, name, description
    )
  }
}

val ideaWithTypeParser = ideaParser ~ typeParser map {
  case idea~kind => (idea.copy(kind=kind))
}

and this is the code using it:

def ideaById(id: Long): Option[IdeaTest] = {
  DB.withConnection { implicit connection =>
    SQL("""
      select * from
      idea inner join idea_type 
      on idea.idea_type_id = idea_type.id 
      where idea.id = {id}""").
      on('id -> id).
      as(ideaParser.singleOpt)
  }
}

The only trouble I see, is that I have to create the IdeaTest object in an inconsistent state (with no ideaType) and then copy it to another instance with the correct IdeaType.

Ruphina answered 2/10, 2012 at 7:3 Comment(1)
This answer was really useful and helped solidify my understanding of composing parsers, thanks!Sixpence

© 2022 - 2024 — McMap. All rights reserved.