Adventures in Play 2.0 with Scala, Salat, and Casbah


How to get play-salat and play-cashbah working in the Play 2.0 Scala web framework

Scala?
Scala has anonymous functions, type inference, list comprehensions, lazy initialization, excellent pattern matching, case classes, delimited continuations, higher-order types, and complexity that exceeds 10.

What is that stuff after the mapping binder? The apply and unapply functions of the Post model are curried with the form mapping. Check out the views.html.helper package in the Play 2.0 API.

Controller: controllers/Posts.scala
val postForm: Form[Post] = Form (
mapping(
"name" -> nonEmptyText,
"description" -> nonEmptyText,
"group" -> ignored(Group.findOneByName("Test group").get.id),
"updated" -> optional(date)
)((name, description, group, updated) => Post(new ObjectId, name, description, Option(group), new java.util.Date, updated))
((post: Post) => Some((post.name, post.description, post.group.get, post.updated)))
)
def create = Action { implicit request =>
postForm.bindFromRequest.fold(
errors => BadRequest(views.html.posts.list(Post.findAll().toList, errors)),
value => {
Post.save(value)
Redirect(routes.Posts.list)
}
)
}
view raw Posts.scala hosted with ❤ by GitHub

View: views/posts.scala.html
@(posts: List[Post], postForm: Form[Post])(implicit flash: Flash)
@main(Messages("posts.list")) {
<dl class="posts">
@for(post <- posts) {
<dt>
<a href="@controllers.routes.Posts.view(post.id)">@post.name</a>
</dt>
<dd>@post.description</dd>
}
</dl>
<p><a href="@controllers.routes.Posts.create()" class="btn">
<span class="icon-plus"></span> @Messages("posts.new.command")</a></p>
@helper.form(routes.Posts.create) {
@helper.inputText(postForm("name"))
@helper.inputText(postForm("description"))
@helper.inputText(postForm("group_id"))
<input type="submit" value="Create">
}
}

Model: models/Post.scala
package models
import play.api.Play.current
import java.util.Date
import com.novus.salat._
import com.novus.salat.annotations._
import com.novus.salat.dao._
import com.mongodb.casbah.Imports._
import se.radley.plugin.salat._
import mongoContext._
case class Post(
id: ObjectId = new ObjectId,
name: String,
description: String,
@Key("group_id")group: Option[ObjectId] = None,
created: Date = new Date(),
updated: Option[Date] = None
) {
override def toString = "%s - %s".format(id, name)
override def toSlug = {
val Slugger = """[\./]\?:[^/?#\.]+\?""".r
name match {
case Slugger(c) => "slug: " + c
case _ => "slug none"
}
}
}
object Post extends ModelCompanion[Post, ObjectId] {
val dao = new SalatDAO[Post, ObjectId](collection = mongoCollection("posts")) {}
def findOneByName(name: String): Option[Post] = dao.findOne(MongoDBObject("name" -> name))
def findByGroup(group: String) = dao.find(MongoDBObject.empty) //.toList
def findGroup(id: Option[ObjectId]): Option[Group] = Group.findOneById(id.get)
}
view raw Post.scala hosted with ❤ by GitHub

Context (from play-salat): models/mongoContext.scala
package models
import com.novus.salat.dao._
import com.novus.salat.annotations._
import com.mongodb.casbah.Imports._
import com.novus.salat.{TypeHintFrequency, StringTypeHintStrategy, Context}
import play.api.Play
import play.api.Play.current
package object mongoContext {
implicit val context = {
val context = new Context {
val name = "global"
override val typeHintStrategy = StringTypeHintStrategy(when = TypeHintFrequency.WhenNecessary, typeHint = "_t")
}
context.registerGlobalKeyOverride(remapThis = "id", toThisInstead = "_id")
context.registerClassLoader(Play.classloader)
context
}
}


Typical Scala user



Complexity exceeds 10

No comments:

Post a Comment