In this chapter, we will be covering the following topics:
All the supported routes are specified within a single file: routes
(by default). This makes it all the easier to figure out which one would be ideal.
The routes
file is compiled and if any errors occur, the compilation fails.
However, the routes
file is not a Scala object. So how does the compiler know what to do with the routes
file? To find this out, let's perform the following steps:
index.scala.html
home page as follows:<!DOCTYPE html> <html> <head> <title>Home</title> </head> <body> <h1>Hello, World!</h1> </body> </html>
package controllers import play.api.mvc._ object AppController extends Controller { def index = Action { Ok(views.html.index()) } }
routes
file:# Home page GET / controllers.AppController.index
routes_routing.scala
file is now available in the HelloWorld/target/scala-2.10/src_managed/main
directory. The contents of the file will be similar to the following code snippet:import play.core._ import play.core.Router._ import play.core.j._ import play.api.mvc._ import Router.queryString object Routes extends Router.Routes { private var _prefix = "/" def setPrefix(prefix: String) { _prefix = prefix List[(String,Routes)]().foreach { case (p, router) => router.setPrefix(prefix + (if(prefix.endsWith("/")) "" else "/") + p) } } def prefix = _prefix lazy val defaultPrefix = { if(Routes.prefix.endsWith("/")) "" else "/" } // @LINE:5 private[this] lazy val controllers_AppController_index0 = Route("GET", PathPattern(List(StaticPart(Routes.prefix)))) def documentation = List(("""GET""", prefix,"""controllers.AppController.index""")).foldLeft(List.empty[(String,String,String)]) { (s,e) => e.asInstanceOf[Any] match { case r @ (_,_,_) => s :+ r.asInstanceOf[(String,String,String)] case l => s ++ l.asInstanceOf[List[(String,String,String)]] }} def routes:PartialFunction[RequestHeader,Handler] = { // @LINE:5 case controllers_AppController_index0(params) => { call { invokeHandler(controllers.AppController.index, HandlerDef(this, "controllers.AppController", "index", Nil,"GET", """ Routes This file defines all application routes (Higher priority routes first) ~~~~ Home page""", Routes.prefix + """""")) } } } }
So, Play generates Scala code from the routes
file. A routes
partial function is created using the routes file. The call
method takes a function that returns a handler and defines the parameters to be passed to it. It is defined to handle 0 to 21 parameters.
The invokeHandler
method is defined as follows:
def invokeHandler[T](call: => T, handler: HandlerDef)(implicit d: HandlerInvoker[T]): Handler = { d.call(call, handler) match { case javaAction: play.core.j.JavaAction => new play.core.j.JavaAction with RequestTaggingHandler { def invocation = javaAction.invocation val annotations = javaAction.annotations val parser = javaAction.annotations.parser def tagRequest(rh: RequestHeader) = doTagRequest(rh, handler) } case action: EssentialAction => new EssentialAction with RequestTaggingHandler { def apply(rh: RequestHeader) = action(rh) def tagRequest(rh: RequestHeader) = doTagRequest(rh, handler) } case ws @ WebSocket(f) => { WebSocket[ws.FRAMES_TYPE](rh => f(doTagRequest(rh, handler)))(ws.frameFormatter) } case handler => handler }
The result from d.call
(call and handler) is matched to the predefined play.core.j.JavaAction
, EssentialAction
, and WebSocket
types (all of which extend the handler trait) and their result is returned.
HandlerDef
is a class, which is defined as follows:
case class HandlerDef(ref: AnyRef, routerPackage: String, controller: String, method: String, parameterTypes: Seq[Class[_]], verb: String, comments: String, path: String)
Let's have a look at how the routes_routing.scala
file is generated.
Play utilizes the features provided by Simple Build Tool (SBT) to add a source generation task. A source generation task should generate sources in a subdirectory of sourceManaged
and return a sequence of the files generated.
The SBT documentation can be found at http://www.scala-sbt.org/0.13.2/docs/Howto/generatefiles.html.
The usage can be seen in PlaySettings.scala
, as follows:
sourceGenerators in Compile <+= (state, confDirectory, sourceManaged in Compile, routesImport, generateReverseRouter, generateRefReverseRouter, namespaceReverseRouter) map { (s, cd, sm, ri, grr, grrr, nrr) => RouteFiles(s, Seq(cd), sm, ri, grr, grrr, nrr) },
RouteFiles
is defined in the PlaySourceGenerators
trait, which handles the Scala code generation for routes and views. Yes, even views are transformed to Scala code. For example, an index.template.scala
file is available for the HelloWorld
project at HelloWorld/target/scala-2.10/src_managed/main/views/html
.
The definition for RouteFiles
calls the RoutesCompiler.compile
method and then returns the file paths where the source will be generated. The compile
method parses the file using RouteFileParser
and then generates the Scala code using the generateRouter
method.
18.191.181.252