Permalink
Browse files

Implemented support for routing code samples

  • Loading branch information...
1 parent b3ce8d7 commit d07512d7d4f05a981959a1fde09304e01562c1c0 @jroper jroper committed Apr 10, 2013
@@ -19,18 +19,13 @@ Routes are defined in the `conf/routes` file, which is compiled. This means that
Let’s see what a route definition looks like:
-```
-GET /clients/:id controllers.Clients.show(id: Long)
-```
+@[clients-show](code/scalaguide.http.routing.routes)
Each route starts with the HTTP method, followed by the URI pattern. The last element is the call definition.
You can also add comments to the route file, with the `#` character.
-```
-# Display a client.
-GET /clients/:id controllers.Clients.show(id: Long)
-```
+@[clients-show-comment](code/scalaguide.http.routing.routes)
## The HTTP method
@@ -44,17 +39,13 @@ The URI pattern defines the route’s request path. Parts of the request path ca
For example, to exactly match incoming `GET /clients/all` requests, you can define this route:
-```
-GET /clients/all controllers.Clients.list()
-```
+@[static-path](code/scalaguide.http.routing.routes)
### Dynamic parts
If you want to define a route that retrieves a client by ID, you’ll need to add a dynamic part:
-```
-GET /clients/:id controllers.Clients.show(id: Long)
-```
+@[clients-show](code/scalaguide.http.routing.routes)
> Note that a URI pattern may have more than one dynamic part.
@@ -64,95 +55,63 @@ The default matching strategy for a dynamic part is defined by the regular expre
If you want a dynamic part to capture more than one URI path segment, separated by forward slashes, you can define a dynamic part using the `*id` syntax, which uses the `.*` regular expression:
-```
-GET /files/*name controllers.Application.download(name)
-```
+@[spanning-path](code/scalaguide.http.routing.routes)
Here for a request like `GET /files/images/logo.png`, the `name` dynamic part will capture the `images/logo.png` value.
### Dynamic parts with custom regular expressions
You can also define your own regular expression for the dynamic part, using the `$id<regex>` syntax:
-```
-GET /clients/$id<[0-9]+> controllers.Clients.show(id: Long)
-```
+@[regex-path](code/scalaguide.http.routing.routes)
## Call to the Action generator method
The last part of a route definition is the call. This part must define a valid call to a method returning a `play.api.mvc.Action` value, which will typically be a controller action method.
If the method does not define any parameters, just give the fully-qualified method name:
-```
-GET / controllers.Application.homePage()
-```
+@[home-page](code/scalaguide.http.routing.routes)
If the action method defines some parameters, all these parameter values will be searched for in the request URI, either extracted from the URI path itself, or from the query string.
-```
-# Extract the page parameter from the path.
-GET /:page controllers.Application.show(page)
-```
+@[page](code/scalaguide.http.routing.routes)
Or:
-```
-# Extract the page parameter from the query string.
-GET / controllers.Application.show(page)
-```
+@[page](code/scalaguide.http.routing.query.routes)
Here is the corresponding, `show` method definition in the `controllers.Application` controller:
-```scala
-def show(page: String) = Action {
- loadContentFromDatabase(page).map { htmlContent =>
- Ok(htmlContent).as("text/html")
- }.getOrElse(NotFound)
-}
-```
+@[show-page-action](code/ScalaRouting.scala)
### Parameter types
For parameters of type `String`, typing the parameter is optional. If you want Play to transform the incoming parameter into a specific Scala type, you can explicitly type the parameter:
-```
-GET /client/:id controllers.Clients.show(id: Long)
-```
+@[clients-show](code/scalaguide.http.routing.routes)
And do the same on the corresponding `show` method definition in the `controllers.Clients` controller:
-```scala
-def show(id: Long) = Action {
- Client.findById(id).map { client =>
- Ok(views.html.Clients.display(client))
- }.getOrElse(NotFound)
-}
-```
+@[show-client-action](code/ScalaRouting.scala)
### Parameters with fixed values
Sometimes you’ll want to use a fixed value for a parameter:
-```
-# Extract the page parameter from the path, or fix the value for /
-GET / controllers.Application.show(page = "home")
-GET /:page controllers.Application.show(page)
-```
+@[page](code/scalaguide.http.routing.fixed.routes)
### Parameters with default values
You can also provide a default value that will be used if no value is found in the incoming request:
-```
-# Pagination links, like /clients?page=3
-GET /clients controllers.Clients.list(page: Int ?= 1)
-```
+@[clients](code/scalaguide.http.routing.defaultvalue.routes)
### Optional parameters
You can also specify an optional parameter that does not need to be present in all requests:
+@[optional](code/scalaguide.http.routing.routes)
```
# The version parameter is optional. E.g. /api/list-all?version=3.0
GET /api/list-all controllers.Api.list(version: Option[String])
@@ -0,0 +1,111 @@
+package scalaguide.http.routing
+
+import org.specs2.mutable.Specification
+import play.api.test.FakeRequest
+import play.api.mvc._
+import play.api.test.Helpers._
+import play.core.Router
+
+package controllers {
+
+ object Client {
+ def findById(id: Long) = Some("showing client " + id)
+ }
+
+ object Clients extends Controller {
+
+ // #show-client-action
+ def show(id: Long) = Action {
+ Client.findById(id).map { client =>
+ Ok(views.html.Clients.display(client))
+ }.getOrElse(NotFound)
+ }
+ // #show-client-action
+
+ def list() = Action(Ok("all clients"))
+ }
+
+ object Application extends Controller {
+ def download(name: String) = Action(Ok("download " + name))
+ def homePage() = Action(Ok("home page"))
+
+ def loadContentFromDatabase(page: String) = Some("showing page " + page)
+
+ // #show-page-action
+ def show(page: String) = Action {
+ loadContentFromDatabase(page).map { htmlContent =>
+ Ok(htmlContent).as("text/html")
+ }.getOrElse(NotFound)
+ }
+ // #show-page-action
+ }
+
+ object Items extends Controller {
+ def show(id: Long) = Action(Ok("showing item " + id))
+ }
+
+ object Api extends Controller {
+ def list(version: Option[String]) = Action(Ok("version " + version))
+ }
+}
+
+package query {
+ package object controllers {
+ val Application = scalaguide.http.routing.controllers.Application
+ }
+}
+
+package fixed {
+ package object controllers {
+ val Application = scalaguide.http.routing.controllers.Application
+ }
+}
+
+package defaultvalue.controllers {
+ object Clients extends Controller {
+ def list(page: Int) = Action(Ok("clients page " + page))
+ }
+}
+
+object ScalaRoutingSpec extends Specification {
+ "the scala router" should {
+ "support simple routing with a long parameter" in {
+ contentOf(FakeRequest("GET", "/clients/10")).trim must_== "showing client 10"
+ }
+ "support a static path" in {
+ contentOf(FakeRequest("GET", "/clients/all")) must_== "all clients"
+ }
+ "support a path part that spans multiple segments" in {
+ contentOf(FakeRequest("GET", "/files/foo/bar")) must_== "download foo/bar"
+ }
+ "support regex path parts" in {
+ contentOf(FakeRequest("GET", "/items/20")) must_== "showing item 20"
+ }
+ "support parameterless actions" in {
+ contentOf(FakeRequest("GET", "/")) must_== "home page"
+ }
+ "support passing parameters from the path" in {
+ contentOf(FakeRequest("GET", "/foo")) must_== "showing page foo"
+ }
+ "support passing parameters from the query string" in {
+ contentOf(FakeRequest("GET", "/?page=foo"), query.Routes) must_== "showing page foo"
+ }
+ "support fixed values for parameters" in {
+ contentOf(FakeRequest("GET", "/foo"), fixed.Routes) must_== "showing page foo"
+ contentOf(FakeRequest("GET", "/"), fixed.Routes) must_== "showing page home"
+ }
+ "support default values for parameters" in {
+ contentOf(FakeRequest("GET", "/clients"), defaultvalue.Routes) must_== "clients page 1"
+ contentOf(FakeRequest("GET", "/clients?page=2"), defaultvalue.Routes) must_== "clients page 2"
+ }
+ "support optional values for parameters" in {
+ contentOf(FakeRequest("GET", "/api/list-all")) must_== "version None"
+ contentOf(FakeRequest("GET", "/api/list-all?version=3.0")) must_== "version Some(3.0)"
+ }
+
+ }
+
+ def contentOf(rh: RequestHeader, router: Router.Routes = Routes) = contentAsString(router.routes(rh) match {
+ case e: EssentialAction => AsyncResult(e(rh).run)
+ })
+}
@@ -0,0 +1,4 @@
+# #clients
+# Pagination links, like /clients?page=3
+GET /clients controllers.Clients.list(page: Int ?= 1)
+# #clients
@@ -0,0 +1,5 @@
+# #page
+# Extract the page parameter from the path, or fix the value for /
+GET / controllers.Application.show(page = "home")
+GET /:page controllers.Application.show(page)
+# #page
@@ -0,0 +1,4 @@
+# #page
+# Extract the page parameter from the query string.
+GET / controllers.Application.show(page)
+# #page
@@ -0,0 +1,32 @@
+
+# #static-path
+GET /clients/all controllers.Clients.list()
+# #static-path
+
+# #clients-show-comment
+# Display a client.
+# #clients-show ###skip
+GET /clients/:id controllers.Clients.show(id: Long)
+# #clients-show #clients-show-comment
+
+# #spanning-path
+GET /files/*name controllers.Application.download(name)
+# #spanning-path
+
+# #regex-path
+GET /items/$id<[0-9]+> controllers.Items.show(id: Long)
+# #regex-path
+
+# #home-page
+GET / controllers.Application.homePage()
+# #home-page
+
+# #page
+# Extract the page parameter from the path.
+GET /:page controllers.Application.show(page)
+# #page
+
+# #optional
+# The version parameter is optional. E.g. /api/list-all?version=3.0
+GET /api/list-all controllers.Api.list(version: Option[String])
+# #optional
@@ -6,7 +6,7 @@ import PlaySourceGenerators._
object ApplicationBuild extends Build {
- val main = Project("Play-Documentation", file(".")).settings(
+ lazy val main = Project("Play-Documentation", file(".")).settings(
version := PlayVersion.current,
scalaVersion := PlayVersion.scalaVersion,
libraryDependencies ++= Seq(
@@ -28,6 +28,14 @@ object ApplicationBuild extends Build {
ds.flatMap(d => ScalaTemplates(s, d, g, t, defaultTemplatesImport ++ defaultScalaTemplatesImport))
},
+ sourceGenerators in Test <+= (state, javaManualSourceDirectories, sourceManaged in Test) map { (s, ds, g) =>
+ ds.flatMap(d => RouteFiles(s, d, g, Seq("play.libs.F"), false))
+ },
+ sourceGenerators in Test <+= (state, scalaManualSourceDirectories, sourceManaged in Test) map { (s, ds, g) =>
+ ds.flatMap(d => RouteFiles(s, d, g, Seq(), false))
+ },
+
+
templatesTypes := {
case "html" => ("play.api.templates.Html", "play.api.templates.HtmlFormat")
case "txt" => ("play.api.templates.Txt", "play.api.templates.TxtFormat")
@@ -39,4 +47,6 @@ object ApplicationBuild extends Build {
lazy val javaManualSourceDirectories = SettingKey[Seq[File]]("java-manual-source-directories")
lazy val scalaManualSourceDirectories = SettingKey[Seq[File]]("scala-manual-source-directories")
+ // We can't use the Play SBT routes compiler function, since we need to do some special stuff with imports
+
}
Oops, something went wrong.

0 comments on commit d07512d

Please sign in to comment.