diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-08-08 22:21:23 +0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-08-08 22:21:23 +0800 |
commit | b1969928a179bfa833cab528d544a1f77cf24987 (patch) | |
tree | 72db5f6056d61c5e3047cd26b3dd470641b2f63d /readme.md | |
parent | 80b83687269ec8bd18fe156ff985bddcd1d5089a (diff) | |
download | cask-b1969928a179bfa833cab528d544a1f77cf24987.tar.gz cask-b1969928a179bfa833cab528d544a1f77cf24987.tar.bz2 cask-b1969928a179bfa833cab528d544a1f77cf24987.zip |
add full stack todomvc example to readme
Diffstat (limited to 'readme.md')
-rw-r--r-- | readme.md | 199 |
1 files changed, 198 insertions, 1 deletions
@@ -540,4 +540,201 @@ While this example is specific to Quill, you can easily modify the you happen to be using. For libraries which need an implicit transaction, it can be passed into each endpoint function as an additional parameter list as described in -[Extending Endpoints with Decorators](#extending-endpoints-with-decorators).
\ No newline at end of file +[Extending Endpoints with Decorators](#extending-endpoints-with-decorators). + +TodoMVC Full Stack Web +---------------------- + +The following code snippet is the complete code for a full-stack TodoMVC +implementation: including HTML generation for the web UI via +[Scalatags](https://github.com/lihaoyi/scalatags), Javascript for the +interactivity, static file serving, and database integration via +[Quill](https://github.com/getquill/quill). While slightly long, this example +should give you a tour of all the things you need to know to use Cask. + +Note that this is a "boring" server-side-rendered webapp with Ajax interactions, +without any complex front-end frameworks or libraries: it's purpose is to +demonstrate a simple working web application of using Cask end-to-end, which you +can build upon to create your own Cask web application architected however you +would like. + +```scala +import cask.internal.Router +import com.typesafe.config.ConfigFactory +import io.getquill.{SnakeCase, SqliteJdbcContext} +import scalatags.Text.all._ +import scalatags.Text.tags2 +object Server extends cask.MainRoutes{ + val tmpDb = java.nio.file.Files.createTempDirectory("todo-cask-sqlite") + + object ctx extends SqliteJdbcContext( + SnakeCase, + ConfigFactory.parseString( + s"""{"driverClassName":"org.sqlite.JDBC","jdbcUrl":"jdbc:sqlite:$tmpDb/file.db"}""" + ) + ) + + class transactional extends cask.Decorator{ + class TransactionFailed(val value: Router.Result.Error) extends Exception + def wrapFunction(pctx: cask.ParamContext, delegate: Delegate): Returned = { + try ctx.transaction( + delegate(Map()) match{ + case Router.Result.Success(t) => Router.Result.Success(t) + case e: Router.Result.Error => throw new TransactionFailed(e) + } + ) + catch{case e: TransactionFailed => e.value} + } + } + + case class Todo(id: Int, checked: Boolean, text: String) + + ctx.executeAction( + """CREATE TABLE todo ( + | id INTEGER PRIMARY KEY AUTOINCREMENT, + | checked BOOLEAN, + | text TEXT + |); + |""".stripMargin + ) + ctx.executeAction( + """INSERT INTO todo (checked, text) VALUES + |(1, 'Get started with Cask'), + |(0, 'Profit!'); + |""".stripMargin + ) + + import ctx._ + + @transactional + @cask.post("/list/:state") + def list(state: String) = renderBody(state).render + + @transactional + @cask.post("/add/:state") + def add(state: String, request: cask.Request) = { + val body = new String(request.data.readAllBytes()) + run(query[Todo].insert(_.checked -> lift(false), _.text -> lift(body)).returning(_.id)) + renderBody(state).render + } + + @transactional + @cask.post("/delete/:state/:index") + def delete(state: String, index: Int) = { + run(query[Todo].filter(_.id == lift(index)).delete) + renderBody(state).render + } + + @transactional + @cask.post("/toggle/:state/:index") + def toggle(state: String, index: Int) = { + run(query[Todo].filter(_.id == lift(index)).update(p => p.checked -> !p.checked)) + renderBody(state).render + } + + @transactional + @cask.post("/clear-completed/:state") + def clearCompleted(state: String) = { + run(query[Todo].filter(_.checked).delete) + renderBody(state).render + } + + @transactional + @cask.post("/toggle-all/:state") + def toggleAll(state: String) = { + val next = run(query[Todo].filter(_.checked).size) != 0 + run(query[Todo].update(_.checked -> !lift(next))) + renderBody(state).render + } + + def renderBody(state: String) = { + val filteredTodos = state match{ + case "all" => run(query[Todo]).sortBy(-_.id) + case "active" => run(query[Todo].filter(!_.checked)).sortBy(-_.id) + case "completed" => run(query[Todo].filter(_.checked)).sortBy(-_.id) + } + frag( + header(cls := "header", + h1("todos"), + input(cls := "new-todo", placeholder := "What needs to be done?", autofocus := "") + ), + tags2.section(cls := "main", + input( + id := "toggle-all", + cls := "toggle-all", + `type` := "checkbox", + if (run(query[Todo].filter(_.checked).size != 0)) checked else () + ), + label(`for` := "toggle-all","Mark all as complete"), + ul(cls := "todo-list", + for(todo <- filteredTodos) yield li( + if (todo.checked) cls := "completed" else (), + div(cls := "view", + input( + cls := "toggle", + `type` := "checkbox", + if (todo.checked) checked else (), + data("todo-index") := todo.id + ), + label(todo.text), + button(cls := "destroy", data("todo-index") := todo.id) + ), + input(cls := "edit", value := todo.text) + ) + ) + ), + footer(cls := "footer", + span(cls := "todo-count", + strong(run(query[Todo].filter(!_.checked).size).toInt), + " items left" + ), + ul(cls := "filters", + li(cls := "todo-all", + a(if (state == "all") cls := "selected" else (), "All") + ), + li(cls := "todo-active", + a(if (state == "active") cls := "selected" else (), "Active") + ), + li(cls := "todo-completed", + a(if (state == "completed") cls := "selected" else (), "Completed") + ) + ), + button(cls := "clear-completed","Clear completed") + ) + ) + } + + @transactional + @cask.get("/") + def index() = { + cask.Response( + "<!doctype html>" + html(lang := "en", + head( + meta(charset := "utf-8"), + meta(name := "viewport", content := "width=device-width, initial-scale=1"), + tags2.title("Template • TodoMVC"), + link(rel := "stylesheet", href := "/static/index.css") + ), + body( + tags2.section(cls := "todoapp", renderBody("all")), + footer(cls := "info", + p("Double-click to edit a todo"), + p("Created by ", + a(href := "http://todomvc.com","Li Haoyi") + ), + p("Part of ", + a(href := "http://todomvc.com","TodoMVC") + ) + ), + script(src := "/static/app.js") + ) + ) + ) + } + + @cask.static("/static") + def static() = "example/todo/resources/todo" + + initialize() +} +``` |