0

EDIT: For those who wonder how I plan to solve it according to the accepted answer, see Nested Values here.

I'm using Play Framework with Scala and Reactive Mongo.

Currently I'm creating my case classes and forms like this:

case class Person(
  _id : Option[BSONObjectID],
  name: string,
  city: string)

object Person {
  val form: Form[Person] = Form {
    mapping(
      "_id" -> optional(of[String] verifying pattern(
      """[a-fA-F0-9]{24}""".r,
      "constraint.objectId",
      "error.objectId")),
      "name"-> text,
      "city"-> text)
    { (id,name, city) => Person(id.map(new BSONObjectID(_)), name, city) }
    { person =>Some(person._id.map(_.stringify), person.name, person.city)  }
  }
}

If I was using a simple type in the _id property, like String, I could do something simpler, like:

object Person {
  val form: Form[Person] = Form {
    mapping(
      "_id" -> text,
      "name"-> text,
      "city"-> text
      )(Person.apply)(Person.unapply)
  }
}

So I thought I could create my own apply method that would change the first parameter, using currying. I would define something like this:

def apply2(id: Option[String]) = {
  val bsonid = id.map(new BSONObjectID(_))
  (Person.apply _).curried(bsonid)
}

My theory, which implementation doesn't work, is that I would partially apply a BSONObjectID parameter to the Person.apply function, which value would come from the apply2 parameter called id. It doesn't work.

I'm a lazy guy who doesn't want to type a bunch of things just because now I have a new situation which isn't supported by default... The currying is one of my bets, but any solution that would make it easier to create a Form is acceptable.

I just need a way to identify the object so I can delete or update it after, but the current way is kinda boring to type, and I think that the _id field created by MongoDB is perfect.

Is there a way I could make things easier or I just need to stop being lazy?

Ricardo Pieper
  • 2,613
  • 3
  • 26
  • 40

1 Answers1

1

You can make things easier by creating 2 case classes. One to be used when data entry is in place

case class PersonData(name: String, city: String)

and another one to represent real Person in your model

case class Person(_id: BSONObjectID, name: String, city: String)

and in Person object create method:

def fromData(data: PersonData) = Person(
  id = new BSONObjectID(),
  name = data.name,
  city = data.city)

then your mapping From[PersonData] can be simpler, and You avoid Option[BSONObjectID] flying around your model.

  • Nice! That also solves the problem where my classes would be too coupled with MongoDB. I'll test it when I get home. What do you think about having something like `case class Person(_id: BSONObjectID, data: PersonData)`? – Ricardo Pieper Mar 02 '15 at 13:38
  • 1
    That is also good. From my experience, when your model grows I think it would be nice to keep editable data in *data* field eg. `Person(_id, data, events, connections, etc.)`. Also good pattern is to have special case class for id eg. `PersonId(value: BSONObjectID)`. – LastRobotAlive Mar 02 '15 at 13:56
  • Cool. This way I can make it even easier by using Nested Values. I edited my answer. This way I need to declare only the part that grabs the Id from the form. Thanks again! – Ricardo Pieper Mar 02 '15 at 18:05