Skip to content

Instantly share code, notes, and snippets.

@daggerrz
Created December 15, 2010 00:52
Show Gist options
  • Save daggerrz/741430 to your computer and use it in GitHub Desktop.
Save daggerrz/741430 to your computer and use it in GitHub Desktop.
SproutCore Todos backend in Unfiltered Scala
package com.todos
import net.liftweb.json.JsonAST._
import net.liftweb.json.JsonDSL._
/**
* Non-persistent Unfiltered implementation of the SproutCore back-end.
* See <a href="http://wiki.sproutcore.com/w/page/12413020/Todos%2006-Building%20the%20Backend">SproutCore docs</a>
*/
case class Todo(id: String, description: String, isDone: Boolean) {
def toJson = ("guid" -> url) ~ ("description" -> description) ~ ("isDone" -> isDone)
def url = "/tasks/" + id
}
object Todo {
def fromJson(id: String, body: JValue) : Option[Todo] = (for {
JObject(todo) <- body
JField("description", JString(desc)) <- todo
JField("isDone", JBool(done)) <- todo
} yield Todo(id, desc, done)) headOption
}
object TodosServer {
import unfiltered.request._
import unfiltered.response._
def main(args: Array[String]) {
unfiltered.jetty.Http(8080).filter(unfiltered.filter.Planify{
case Path(Seg("tasks" :: Nil), r) => r match {
case GET(_) => wrap(DB.findAll)
case POST(r) => Todo.fromJson(DB.generateId, JsonBody(r)) match {
case Some(todo) => Location(DB.add(todo).url)
case _ => BadRequest
}
}
case Path(Seg("tasks" :: id :: Nil), r) => r match {
case GET(_) => DB.find(id) match {
case Some(t) => t.toJson
case None => NotFound
}
case DELETE(_) => DB.delete(id) match {
case Some(todo) => todo.toJson
case _ => NotFound
}
case PUT(r) =>
(for {
existing <- DB.find(id)
updated <- Todo.fromJson(id, JsonBody(r))
} yield updated) match {
case Some(updated) => DB.add(updated).toJson
case _ => BadRequest
}
}
}).resources(new java.io.File("tmp/build/").toURL).run
}
implicit def jvalueToJsonResponder[T](json: JValue) : Responder[T] = JsonContent ~> ResponseString(compact(render(json)))
type Convertable = {def toJson: JValue}
def wrap(xs: Seq[Convertable]) : JValue = "content" -> xs.map(_.toJson)
}
object DB {
private var todos: Map[String, Todo] = Map()
private val counter = new java.util.concurrent.atomic.AtomicInteger
def add(todo: Todo) = {
todos = todos + (todo.id -> todo)
todo
}
def find(id: String) = todos.get(id)
def findAll = todos.values.toList
def delete(id: String) : Option[Todo] = todos.get(id) match {
case Some(todo) =>
todos = todos - id
Some(todo)
case _ => None
}
def generateId = System.currentTimeMillis.toString + "-" + counter.addAndGet(1)
}
@n8han
Copy link

n8han commented Dec 15, 2010

"Maybe a common UrlForTask extractor?"

Which part is that exactly?

@daggerrz
Copy link
Author

I ended slapping one together (object TodoUrl). The previous revision had Path(Seg("tasks" :: id :: Nil)) repeated for GET, PUT and DELETE. Comparing to the other SproutCore back-end implementations, I'd argue that this quick and dirty one is among the most concise.

It still looks a little "asymmetric" and repetetive, though. I Inversed the Method and Path patterns (+ some other things). Maybe that's better...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment