All Downloads are FREE. Search and download functionalities are using the official Maven repository.

.circumflex-web.2.0.2.source-code.router.scala Maven / Gradle / Ivy

Go to download

Circumflex Web Framework is a lightweight framework for quick and robust application development using Scala programming language.

There is a newer version: 2.5
Show newest version
package ru.circumflex.web

import ru.circumflex.core._
import util.matching.Regex
import xml.Node

/*!# Routing

Circumflex Web Framework is designed around the _route_ concept. A route is an HTTP
method paired with some matching mechanism and attached block.

Each route defines a `Matcher` which describes the conditions a request must satisfy
in order to be matched. If such conditions are met, an attached block is executed
which yields `RouteResponse` to the client.

Routes are declared right inside the body of `RequestRouter` and are executed one-by-one
until first successful match. If no routes match the request, the `404 NOT FOUND` is sent
to the client (unless `onNoMatch` method is overriden in `CircumflexFilter`).

Inside an attached block you can access `MatchResult` object produced by enclosing route.
Match results are stored in `Context`, you can look them up by name.

Take a look at our test sources at [`circumflex-web/src/test/scala`][tests] to see routers
in action.

   [tests]: http://github.com/inca/circumflex/tree/master/circumflex-web/src/test/scala/
*/

/**
 * Performs request routing for an application.
 *
 * For more information refer to
 * router.scala.
 */
class RequestRouter(var prefix: String = "") {

  implicit def string2response(str: String): RouteResponse =
    new RouteResponse(str)
  implicit def xml2response(xml: Node): RouteResponse = {
    response.contentType("application/xml")
    new RouteResponse("\n" + xml.toString)
  }

  implicit def router2response(router: RequestRouter): RouteResponse =
    sendError(404)

  implicit def string2uriMatcher(str: String): RegexMatcher =
    new RegexMatcher("uri", request.uri, servletContext.getContextPath + prefix + str)
  implicit def regex2uriMatcher(regex: Regex): RegexMatcher =
    new RegexMatcher("uri", request.uri,
      new Regex(servletContext.getContextPath + prefix + regex.toString))

  // Routes
  val get = new Route("get")
  val head = new Route("head")
  val getOrPost = new Route("get", "post")
  val getOrHead = new Route("get", "head")
  val post = new Route("post")
  val put = new Route("put")
  val patch = new Route("patch")
  val delete = new Route("delete")
  val options = new Route("options")
  val any = new Route("*")

  // Filter
  val filter = new FilterRoute

  // Shortcuts
  def error(statusCode: Int = 400, message: String = "No message available."): Nothing =
    sendError(statusCode, message)
  def redirect(url: String, flashes: (String, Any)*): Nothing =
    sendRedirect(url, flashes: _*)
  def uri: MatchResult = ctx.get("uri") match {
    case Some(m: MatchResult) => m
    case None => new MatchResult("uri", "splat" -> request.uri)
  }

  /*!## Subroutes

  Subroutes represent an easy and powerful concept which allows nesting
  routes inside each other without creating additional routers.

  Consider the following example:

      class UsersRouter extends RequestRouter("/users") {
        get("/") = "list all users"
        any("/:userId/+") = User.get(param("userId")) match {
          case Some(u: User) => subroute("/" + u.id()) {
            // continue matching with prefix "/users/:userId"
            get("/profile") = "Profile of user #" + u.id()
            get("/accounts") = "Accounts of user #" + u.id()
            // ...
          }
          case _ => sendError(404)
        }
      }

  When entering `subroute`, specified `newPrefix` is appended to current prefix
  and specified `block` gets executed. All routes inside this block will be matched
  with respect to this new prefix. If no routes match inside specified `block`,
  `404 NOT FOUND` is sent.
  */
  def subroute(newPrefix: String)(block: => Unit): Nothing = {
    prefix += newPrefix
    block
    sendError(404)
  }

}

trait RoutingContext[-T] {
  def matches: Boolean
  protected def dispatch(block: => T): Unit
  def and: RoutingContext[T] = if (matches) this else NopRoute
  def apply(matcher: Matcher): RoutingContext[T] = matcher.apply() match {
    case Some(matchResults) if matches =>
      matchResults.foreach(m => ctx.update(m.name, m))
      return this
    case _ => return NopRoute
  }
  def apply(condition: => Boolean): RoutingContext[T] =
    if (matches && condition) this else NopRoute
  def update(matcher: Matcher, block: => T): Unit =
    apply(matcher).dispatch(block)
  def update(condition: => Boolean, block: => T): Unit =
    apply(condition).dispatch(block)
}

class Route(matchingMethods: String*) extends RoutingContext[RouteResponse] {
  val matches = matchingMethods.contains("*") || matchingMethods.contains(request.method)
  protected def dispatch(block: => RouteResponse): Unit = {
    val response = block.body
    send(response)
  }
}

class FilterRoute extends RoutingContext[Unit] {
  def matches = true
  protected def dispatch(block: => Unit) = block
}

object NopRoute extends RoutingContext[Any] {
  protected def dispatch(block: => Any): Unit = {}
  def matches = false
}

/**
 * @see RequestRouter
 */
case class RouteResponse(val body: String)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy