summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cask/test/src/test/cask/ExampleTests.scala26
-rw-r--r--cask/test/src/test/cask/TodoMvcApi.scala39
-rw-r--r--readme.md52
3 files changed, 116 insertions, 1 deletions
diff --git a/cask/test/src/test/cask/ExampleTests.scala b/cask/test/src/test/cask/ExampleTests.scala
index 1a5cf23..1196122 100644
--- a/cask/test/src/test/cask/ExampleTests.scala
+++ b/cask/test/src/test/cask/ExampleTests.scala
@@ -91,5 +91,31 @@ object ExampleTests extends TestSuite{
requests.get(host + "/internal-extra/goo").text() ==> "goo[haoyi]31337"
}
+ 'TodoMvcApi - test(TodoMvcApi){ host =>
+ requests.get(host + "/list/all").text() ==>
+ """[{"checked":true,"text":"Get started with Cask"},{"checked":false,"text":"Profit!"}]"""
+ requests.get(host + "/list/active").text() ==>
+ """[{"checked":false,"text":"Profit!"}]"""
+ requests.get(host + "/list/completed").text() ==>
+ """[{"checked":true,"text":"Get started with Cask"}]"""
+
+ requests.post(host + "/toggle/1")
+
+ requests.get(host + "/list/all").text() ==>
+ """[{"checked":true,"text":"Get started with Cask"},{"checked":true,"text":"Profit!"}]"""
+
+ requests.get(host + "/list/active").text() ==>
+ """[]"""
+
+ requests.post(host + "/add", data = "new Task")
+
+ requests.get(host + "/list/active").text() ==>
+ """[{"checked":false,"text":"new Task"}]"""
+
+ requests.post(host + "/delete/0")
+
+ requests.get(host + "/list/active").text() ==>
+ """[]"""
+ }
}
}
diff --git a/cask/test/src/test/cask/TodoMvcApi.scala b/cask/test/src/test/cask/TodoMvcApi.scala
new file mode 100644
index 0000000..74a5b9b
--- /dev/null
+++ b/cask/test/src/test/cask/TodoMvcApi.scala
@@ -0,0 +1,39 @@
+package test.cask
+
+object TodoMvcApi extends cask.MainRoutes{
+ case class Todo(checked: Boolean, text: String)
+ object Todo{
+ implicit def todoRW = upickle.default.macroRW[Todo]
+ }
+ var todos = Seq(
+ Todo(true, "Get started with Cask"),
+ Todo(false, "Profit!")
+ )
+
+ @cask.get("/list/:state")
+ def list(state: String) = {
+ val filteredTodos = state match{
+ case "all" => todos
+ case "active" => todos.filter(!_.checked)
+ case "completed" => todos.filter(_.checked)
+ }
+ upickle.default.write(filteredTodos)
+ }
+
+ @cask.post("/add")
+ def add(request: cask.Request) = {
+ todos = Seq(Todo(false, new String(request.data.readAllBytes()))) ++ todos
+ }
+
+ @cask.post("/toggle/:index")
+ def toggle(index: Int) = {
+ todos = todos.updated(index, todos(index).copy(checked = !todos(index).checked))
+ }
+
+ @cask.post("/delete/:index")
+ def delete(index: Int) = {
+ todos = todos.patch(index, Nil, 1)
+ }
+
+ initialize()
+}
diff --git a/readme.md b/readme.md
index 3774c8e..e1942d5 100644
--- a/readme.md
+++ b/readme.md
@@ -359,4 +359,54 @@ Decorators are useful for things like:
fails), or access to some system resource that needs to be released.
Writing Custom Endpoints
------------------------- \ No newline at end of file
+------------------------
+
+TodoMVC Api Server
+------------------
+
+```scala
+object TodoMvcApi extends cask.MainRoutes{
+ case class Todo(checked: Boolean, text: String)
+ object Todo{
+ implicit def todoRW = upickle.default.macroRW[Todo]
+ }
+ var todos = Seq(
+ Todo(true, "Get started with Cask"),
+ Todo(false, "Profit!")
+ )
+
+ @cask.get("/list/:state")
+ def list(state: String) = {
+ val filteredTodos = state match{
+ case "all" => todos
+ case "active" => todos.filter(!_.checked)
+ case "completed" => todos.filter(_.checked)
+ }
+ upickle.default.write(filteredTodos)
+ }
+
+ @cask.post("/add")
+ def add(request: cask.Request) = {
+ todos = Seq(Todo(false, new String(request.data.readAllBytes()))) ++ todos
+ }
+
+ @cask.post("/toggle/:index")
+ def toggle(index: Int) = {
+ todos = todos.updated(index, todos(index).copy(checked = !todos(index).checked))
+ }
+
+ @cask.post("/delete/:index")
+ def delete(index: Int) = {
+ todos = todos.patch(index, Nil, 1)
+ }
+
+ initialize()
+}
+```
+
+This is a simple self-contained example of using Cask to write an API server for
+the common [TodoMVC example app](http://todomvc.com/).
+
+This minimal example intentionally does not contain javascript, HTML, styles,
+etc.. Those can be managed via the normal mechanism for
+[Serving Static Files](#serving-static-files).