Created
December 15, 2010 00:52
-
-
Save daggerrz/741430 to your computer and use it in GitHub Desktop.
SproutCore Todos backend in Unfiltered Scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
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
"Maybe a common UrlForTask extractor?"
Which part is that exactly?