Skip to content

Commit

Permalink
Extract the core DSL from the kernel.
Browse files Browse the repository at this point in the history
  • Loading branch information
rossabaker committed Aug 11, 2011
1 parent cf6ed4f commit 934f019
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 81 deletions.
214 changes: 214 additions & 0 deletions core/src/main/scala/org/scalatra/CoreDsl.scala
@@ -0,0 +1,214 @@
package org.scalatra

import java.lang.{Integer => JInteger}
import javax.servlet.ServletContext
import javax.servlet.http.{HttpServletRequest, HttpServletResponse, HttpSession}

import ScalatraKernel.MultiParams

/**
* The core DSL of a Scalatra application.
*/
trait CoreDsl {
/**
* The current servlet context
*/
implicit def servletContext: ServletContext

/**
* The current request
*/
implicit def request: HttpServletRequest

/**
* A map of the current parameters. The map contains the head of every
* non-empty value in `multiParams`.
*/
def params: Map[String, String]

/**
* A multi-map of the current parameters. Parameters may come from:
* - the query string
* - the POST body
* - the route matchers of the currently executing route
*
* The map has a default value of `Seq.empty`.
*/
def multiParams: MultiParams

/**
* The current response.
*/
implicit def response: HttpServletResponse

/**
* Gets the content type of the current response.
*/
def contentType = response.getContentType

/**
* Sets the content type of the current response.
*/
def contentType_=(contentType: String) = response.setContentType(contentType)

/**
* Sets the status code of the current response.
*/
def status(code: Int) = response.setStatus(code)

/**
* Sends a redirect response.
*/
def redirect(uri: String) = response.sendRedirect(uri)

/**
* The current HTTP session. Creates a session if none exists.
*/
implicit def session: HttpSession = request.getSession

/**
* The current HTTP session. If none exists, None is returned.
*/
def sessionOption: Option[HttpSession] = Option(request.getSession(false))

/**
* Adds a filter to run before the route.
*
* Equivalent to beforeSome()(block).
*/
final def beforeAll(block: => Any): Unit = beforeSome()(block)

@deprecated("Use beforeAll", "2.0")
final def before(block: => Any): Unit = beforeAll(block)

/**
* Adds a filter to run before the route. The filter only runs if each
* routeMatcher returns Some.
*/
def beforeSome(routeMatchers: RouteMatcher*)(block: => Any): Unit

/**
* Adds a filter to run after the route.
*
* Equivalent to afterSome()(block).
*/
final def afterAll(block: => Any): Unit = afterSome()(block)

@deprecated("Use afterAll", "2.0")
final def after(block: => Any): Unit = afterAll(block)

/**
* Adds a filter to run after the route. The filter only runs if each
* routeMatcher returns Some.
*/
def afterSome(routeMatchers: RouteMatcher*)(block: => Any): Unit

/**
* Defines a block to run if no matching routes are found, or if all
* matching routes pass.
*/
def notFound(block: => Any): Unit

/**
* Defines a block to run if matching routes are found only for other
* methods. The set of matching methods is passed to the block.
*/
def methodNotAllowed(block: Set[HttpMethod] => Any): Unit

/**
* Defines a block to run if an exception is thrown. The exception is
* passed to the block.
*/
def error(block: => Any): Unit

/**
* The throwable currently handled by the error block. Invalid outside
* an error block.
*/
def caughtThrowable: Throwable

/**
* The Scalatra DSL core methods take a list of [[org.scalatra.RouteMatcher]]
* and a block as the action body. The return value of the block is
* rendered through the pipeline and sent to the client as the response body.
*
* See [[org.scalatra.ScalatraKernel.renderResponseBody]] for the detailed
* behaviour and how to handle your response body more explicitly, and see
* how different return types are handled.
*
* The block is executed in the context of a CoreDsl instance, so all the
* methods defined in this trait are also available inside the block.
*
* {{{
* get("/") {
* <form action="/echo">
* <label>Enter your name</label>
* <input type="text" name="name"/>
* </form>
* }
*
* post("/echo") {
* "hello {params('name)}!"
* }
* }}}
*
* ScalatraKernel provides implicit transformation from boolean blocks,
* strings and regular expressions to [[org.scalatra.RouteMatcher]], so
* you can write code naturally.
* {{{
* get("/", request.getRemoteHost == "127.0.0.1") { "Hello localhost!" }
* }}}
*
*/
def get(routeMatchers: RouteMatcher*)(block: => Any): Route

/**
* @see get
*/
def post(routeMatchers: RouteMatcher*)(block: => Any): Route

/**
* @see get
*/
def put(routeMatchers: RouteMatcher*)(block: => Any): Route

/**
* @see get
*/
def delete(routeMatchers: RouteMatcher*)(block: => Any): Route

/**
* @see get
*/
def options(routeMatchers: RouteMatcher*)(block: => Any): Route

/**
* @see patch
*/
def patch(routeMatchers: RouteMatcher*)(block: => Any): Route

/**
* Immediately halts the current action. If called within a before filter,
* prevents the action from executing. Any matching after filters will still
* execute.
*
* @param status set as the response's HTTP status code. Ignored if null.
*
* @param body rendered to the response body through the response pipeline.
*
* @param reason set as the HTTP status reason. Ignored if null or if status
* is null.
*
* @param headers added as headers to the response. Previously set headers
* are retained
*/
def halt(status: JInteger = null,
body: Any = (),
headers: Map[String, String] = Map.empty,
reason: String = null): Nothing

/**
* Immediately passes execution to the next matching route.
*/
def pass()
}
2 changes: 1 addition & 1 deletion core/src/main/scala/org/scalatra/ScalatraFilter.scala
Expand Up @@ -25,7 +25,7 @@ trait ScalatraFilter extends Filter with ScalatraKernel with Initializable {

protected var doNotFound: Action = () => filterChain.doFilter(request, response)

protected var servletContext: ServletContext = _
var servletContext: ServletContext = _

type Config = FilterConfig

Expand Down
83 changes: 4 additions & 79 deletions core/src/main/scala/org/scalatra/ScalatraKernel.scala
Expand Up @@ -45,15 +45,10 @@ import ScalatraKernel._
* methods register a new action to a route for a given HTTP method, possibly
* overwriting a previous one. This trait is thread safe.
*/
trait ScalatraKernel extends Handler with Initializable
trait ScalatraKernel extends Handler with CoreDsl with Initializable
{
protected lazy val routes: RouteRegistry = new RouteRegistry

def contentType = response.getContentType
def contentType_=(value: String) {
response.setContentType(value)
}

protected val defaultCharacterEncoding = "UTF-8"
protected val _response = new DynamicVariable[HttpServletResponse](null)
protected val _request = new DynamicVariable[HttpServletRequest](null)
Expand Down Expand Up @@ -150,19 +145,11 @@ trait ScalatraKernel extends Handler with Initializable

def requestPath: String

def beforeAll(fun: => Any) = addBefore(Iterable.empty, fun)
@deprecated("Use beforeAll", "2.0")
def before(fun: => Any) = beforeAll(fun)

def beforeSome(routeMatchers: RouteMatcher*)(fun: => Any) = addBefore(routeMatchers, fun)

private def addBefore(routeMatchers: Iterable[RouteMatcher], fun: => Any) =
routes.appendBeforeFilter(Route(routeMatchers, () => fun))

def afterAll(fun: => Any) = addAfter(Iterable.empty, fun)
@deprecated("Use afterAll", "2.0")
def after(fun: => Any) = afterAll(fun)

def afterSome(routeMatchers: RouteMatcher*)(fun: => Any) = addAfter(routeMatchers, fun)

private def addAfter(routeMatchers: Iterable[RouteMatcher], fun: => Any) =
Expand Down Expand Up @@ -200,7 +187,7 @@ trait ScalatraKernel extends Handler with Initializable
def error(fun: => Any) = errorHandler = { () => fun }

private val _caughtThrowable = new DynamicVariable[Throwable](null)
protected def caughtThrowable = _caughtThrowable.value
def caughtThrowable = _caughtThrowable.value

protected def renderResponse(actionResult: Any) {
if (contentType == null)
Expand Down Expand Up @@ -242,7 +229,7 @@ trait ScalatraKernel extends Handler with Initializable
}

protected[scalatra] val _multiParams = new DynamicVariable[MultiMap](null)
protected def multiParams: MultiParams = (_multiParams.value).withDefaultValue(Seq.empty)
def multiParams: MultiParams = (_multiParams.value).withDefaultValue(Seq.empty)
/*
* Assumes that there is never a null or empty value in multiParams. The servlet container won't put them
* in request.getParameters, and we shouldn't either.
Expand All @@ -252,31 +239,9 @@ trait ScalatraKernel extends Handler with Initializable
}
def params = _params

def redirect(uri: String) = (_response value) sendRedirect uri
implicit def request = _request value
implicit def response = _response value
def session = request.getSession
def sessionOption = request.getSession(false) match {
case s: HttpSession => Some(s)
case null => None
}
def status(code: Int) = (_response value) setStatus code

/**
* Immediately halts the current action. If called within a before filter,
* prevents the action from executing. Any matching after filters will still
* execute.
*
* @param status set as the response's HTTP status code. Ignored if null.
*
* @param body rendered to the response body through the response pipeline.
*
* @param reason set as the HTTP status reason. Ignored if null or if status
* is null.
*
* @param headers added as headers to the response. Previously set headers
* are retained
*/
def halt(status: JInteger = null,
body: Any = (),
headers: Map[String, String] = Map.empty,
Expand Down Expand Up @@ -305,52 +270,12 @@ trait ScalatraKernel extends Handler with Initializable
def pass() = throw new PassException
protected[scalatra] class PassException extends RuntimeException

/**
* The Scalatra DSL core methods take a list of [[org.scalatra.RouteMatcher]] and a block as
* the action body.
* The return value of the block is converted to a string and sent to the client as the response body.
*
* See [[org.scalatra.ScalatraKernel.renderResponseBody]] for the detailed behaviour and how to handle your
* response body more explicitly, and see how different return types are handled.
*
* The block is executed in the context of the ScalatraKernel instance, so all the methods defined in
* this trait are also available inside the block.
*
* {{{
* get("/") {
* <form action="/echo">
* <label>Enter your name</label>
* <input type="text" name="name"/>
* </form>
* }
*
* post("/echo") {
* "hello {params('name)}!"
* }
* }}}
*
* ScalatraKernel provides implicit transformation from boolean blocks, strings and regular expressions
* to [[org.scalatra.RouteMatcher]], so you can write code naturally
* {{{
* get("/", request.getRemoteHost == "127.0.0.1") { "Hello localhost!" }
* }}}
*
*/
def get(routeMatchers: RouteMatcher*)(action: => Any) = addRoute(Get, routeMatchers, action)

/**
* @see [[org.scalatra.ScalatraKernel.get]]
*/
def post(routeMatchers: RouteMatcher*)(action: => Any) = addRoute(Post, routeMatchers, action)

/**
* @see [[org.scalatra.ScalatraKernel.get]]
*/
def put(routeMatchers: RouteMatcher*)(action: => Any) = addRoute(Put, routeMatchers, action)

/**
* @see [[org.scalatra.ScalatraKernel.get]]
*/
def delete(routeMatchers: RouteMatcher*)(action: => Any) = addRoute(Delete, routeMatchers, action)

/**
Expand Down Expand Up @@ -407,7 +332,7 @@ trait ScalatraKernel extends Handler with Initializable
case _ => None
}

protected def servletContext: ServletContext
def servletContext: ServletContext

def environment: String = System.getProperty(EnvironmentKey, initParameter(EnvironmentKey).getOrElse("development"))
def isDevelopmentMode = environment.toLowerCase.startsWith("dev")
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/org/scalatra/ScalatraServlet.scala
Expand Up @@ -29,7 +29,7 @@ abstract class ScalatraServlet
response.getWriter println "Requesting %s but only have %s".format(request.getRequestURI, routes)
}

protected def servletContext: ServletContext = getServletContext
def servletContext: ServletContext = getServletContext

type Config = ServletConfig

Expand Down

0 comments on commit 934f019

Please sign in to comment.