summaryrefslogblamecommitdiff
path: root/example/todo/app/src/TodoServer.scala
blob: 1a768d16f36e1546af27af87def426f082b619d3 (plain) (tree)
1
2
3
4
5
6
7
8
           


                                                 

                           
 
                                          


















                                                                                         

















                                                          

   




                                                    
 
                

                                                   
                                                 






















                                                                                           

   


                                  





                                                           
                                    


                                                                          

         









                                                                                          
          


































                                                                               



       
                







                                                                                     


                                                                
                                                             

                                             

                                                        
              
                         


                                                       





                                         

                                  


              
package app
import cask.internal.Router
import com.typesafe.config.ConfigFactory
import io.getquill.{SnakeCase, SqliteJdbcContext}
import scalatags.Text.all._
import scalatags.Text.tags2

object TodoServer 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.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.staticResources("/static")
  def static() = "todo"

  initialize()
}