Mastering Play Framework For Scala - Sample Chapter
Mastering Play Framework For Scala - Sample Chapter
Mastering Play Framework For Scala - Sample Chapter
ee
$ 49.99 US
32.99 UK
P U B L I S H I N G
Shiti Saxena
pl
C o m m u n i t y
E x p e r i e n c e
D i s t i l l e d
Sa
m
Shiti Saxena
She loves to travel, is a movie buff, and likes to spend time playing her piano whenever
she is not programming.
She has authored Getting Started with SBT for Scala
Chapter 9, Testing, shows you how a Play application can be tested using Specs2 and
ScalaTest. We go through the different helper methods available for simplifying testing
of a Play application.
Chapter 10, Debugging and Logging, is where we configure the debugging of a Play
application in the IDE. In this chapter, you get to learn how to start a Play application in
the Scala console. This chapter also places emphasis on the logging API provided by the
Play Framework and the methods of customizing the log format.
Chapter 11, Web Services and Authentication, explains the WS (WebService) plugin and
the API exposed through it. We also access users' data from the service providers using
OpenID and OAuth 1.0a.
Chapter 12, Play in Production, explains how to deploy a Play application on production.
While deploying the application, we also check the different packaging options (RPM,
Debian, ZIP, Windows, and so on) available by default.
Chapter 13, Writing Play Plugins, gives an explanation of all plugins, with their
declaration, definition, and best practices.
[1]
View: This is the part of the application which is visible to the end user
Controller: This is responsible for processing input from the end user
[2]
Chapter 1
The pattern also defines how these components are supposed to interact with one
another. Let's consider an online store as our application. In this case, the products,
brands, users, cart, and so on can be represented by a model each. The pages in the
application where users can view the products are defined in the views (HTML
pages). When a user adds a product to the cart, the transaction is handled by a
controller. The view is unaware of the model and the model is unaware of the view.
The controller sends commands to the model and view. The following figure shows
how the models, views, and controllers interact:
Play also comes prepackaged with an easy to use Hibernate layer, and offers
OpenID, Ehcache, and web service integration straight out of the box by adding
a dependency on the individual modules.
In the following sections of this chapter, we'll make a simple app using Play. This is
mainly for developers who are using Play earlier.
[3]
Take a look at the project structure. It will be similar to the one shown here:
If we can't use Activator, we will probably have to create all these files. Now, let's dig
into the files individually and see which is for what purpose.
Chapter 1
SBT version lower than 0.13.7 expects a new line as the delimiter
between two different statements in the build definition.
In the preceding build definition, the values for the project's name, version, and
root are specified. Another way of specifying values is by updating the existing
ones. We can append to the existing values using the += symbol for individual items
and ++= for sequences. For example:
resolvers += Resolver.sonatypeRepo("snapshots")
scalacOptions ++= Seq("-feature", "-language:reflectiveCalls")
resolvers is the list of URLs from where the dependencies can be picked up and
scalacOptions is the list of parameters passed to the Scala compiler.
Alternatively, an SBT project can also use a .scala build file. The structure for our
application would then be:
[5]
sbt._
Keys._
play.Play.autoImport._
PlayKeys._
The .scala build definition comes in handy when we need to define custom tasks/
settings for our application/plugin, since it uses Scala code. The .sbt definition is
generally smaller and simpler than its corresponding .scala definition and is hence,
more preferred.
Without the Play settings, which are imported by enabling the PlayScala plugin,
SBT is clueless that our project is a Play application and is defined according to the
semantics of a Play application.
So, is that statement sufficient for SBT to run a Play app correctly?
No, there is something else as well! SBT allows us to extend build definitions using
plugins. Play-based projects make use of the Play SBT plugin and it is from this
plugin that SBT gets the required settings. In order for SBT to download all the
plugins that our project will be using, they should be added explicitly. This is done
by adding them in plugins.sbt in the projectRoot/project directory.
[6]
Chapter 1
Let's take a look at the plugins.sbt file. The file content will be:
resolvers += "Typesafe repository" at
"http://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8")
The parameter passed to addSbtPlugin is the Ivy module ID for the plugin. The
resolver is helpful when the plugin is not hosted on Maven or Typesafe repositories.
The build.properties file is used to specify the SBT version to avoid
incompatibility issues between the same build definitions compiled by using two or
more different versions of SBT.
This covers all the build-related files of a Play application.
[7]
We can provide the title and content from our Scala code to display this view. A
view can be bound to a specific request through the controllers. So, let's update the
code for our controller SampleAppController, as follows:
package controllers
import play.api.mvc._
import play.api.templates.Html
object SampleAppController extends Controller {
def index = Action {
val content = Html("<div>This is the content for the sample
app<div>")
Ok(views.html.main("Home")(content))
}
}
On saving the changes and running the application, we will see the page hosted at
http://localhost:9000, as shown in the screenshot:
[8]
Chapter 1
controllers.SampleAppController.
This means that for a GET request on the / path, we have mapped the response to be
the one returned from the SampleController.index() method.
A sample request would be:
curl 'http://localhost:9000/'
Go ahead and add a few more pages to the application to get more comfortable,
maybe a FAQ, Contact Us, or About.
The request-response cycle for a Play app, explained in the preceding code is
represented here:
[9]
controllers.Assets.at(path="/
[ 10 ]
Chapter 1
In order to use a template, we can call its generated method from our Scala code or
refer to it in other templates by using its name. Using a main template can come in
handy when we want to apply a change to all the templates. For example, if we want
to add a style sheet for an application, just adding this in our main template will
ensure that it's added for all the dependent views.
To view this template's content on loading, update the index method to:
package controllers
import play.api.mvc._
object TaskController extends Controller {
def index = Action {
Ok(views.html.index())
}
}
Now, in order to work on the functionality, let's add a model called Task, which
we'll use to represent the task in our app. Since we want to delete the functionality
too, we will need to identify each task using a unique ID, which means that our
model should have two properties: an ID and a name. The Task model will be:
package models
case class Task(id: Int, name: String)
object Task {
private var taskList: List[Task] = List()
def all: List[Task] = {
taskList
[ 11 ]
In this model, we are using a taskList private variable to keep track of the tasks for
the session.
In the add method, whenever a new task is added, we append it to this list. Instead
of keeping another variable to keep count of the IDs, I choose to increment the ID of
the last element in the list.
In the delete method, we simply filter out the task with the given ID and the all
method returns the list for this session.
Now, we need to call these methods in our controller and then bind them to a
request route. Now, update the controller in this way:
import models.Task
import play.api.mvc._
object TaskController extends Controller {
def index = Action {
Redirect(routes.TaskController.tasks)
}
def tasks = Action {
Ok(views.html.index(Task.all))
}
def newTask = Action(parse.urlFormEncoded) {
implicit request =>
Task.add(request.body.get("taskName").get.head)
Redirect(routes.TaskController.index)
}
def deleteTask(id: Int) = Action {
[ 12 ]
Chapter 1
Task.delete(id)
Ok
}
}
In the preceding code, routes refers to the helper that can be used to access the
routes defined for the application in conf/routes. Try running the app now!
It'll throw a compilation error, which says that values tasks is not a member of
controllers.TaskController.index
# Tasks
GET
POST
/tasks
/tasks
controllers.TaskController.tasks
controllers.TaskController.newTask
We'll complete our application's view so that it can facilitate the following:
accept and render a List[Task]
@(tasks: List[Task])
@main("Task Tracker") {
<h2>Task Tracker</h2>
<div>
<form action="@routes.TaskController.newTask()"
method="post">
<input type="text" name="taskName" placeholder="Add a
new Task" required>
<input type="submit" value="Add">
</form>
</div>
<div>
<ul>
@tasks.map { task =>
<li>
@task.name
</li>
[ 13 ]
We have now added a form in the view, which takes a text input with the taskName
name and submits this data to a TaskController.newTask method.
Notice that we have now added a tasks argument for this template
and are displaying it in the view. Scala elements and predefined
templates are prepended with the @ twirl symbol in the views.
Now, when running the app, we will be able to add tasks as well as view existing
ones, as shown here:
Deleting a task
The only thing remaining in our app is the ability to delete a task. Update the index
template so that each <li> element has a button, whose click results in a delete
request to the server:
<li>
@task.name <button onclick="deleteTask ( @task.id)
;">Remove</button>
</li>
Then, we would need to update the routes file to map the delete action:
DELETE
(id: Int).
/tasks/:id
controllers.TaskController.deleteTask
We also need to define deleteTask in our view. To do this, we can simply add
a script:
<script>
function deleteTask ( id ) {
[ 14 ]
Chapter 1
var req = new XMLHttpRequest ( ) ;
req.open ( "delete", "/tasks/" + id ) ;
req.onload = function ( e ) {
if ( req.status = 200 ) {
document.location.reload ( true ) ;
}
} ;
req.send ( ) ;
}
</script>
Now, when we run the app, we can add tasks as well as remove them, as shown here:
I am leaving the task of beautifying the app up to you. Add a style sheet in the public
directory and declare it in the main template. For example, if the taskTracker.css
file is located at public/stylesheets, the link to it in the main.scala.html file
would be:
<link rel="stylesheet" media="screen"
href="@routes.Assets.at("stylesheets/taskTracker.css")">
[ 15 ]
Summary
This chapter gives a basic introduction to the Play Framework. In this chapter,
we have learned how to build simple applications using the Play Framework. We
have gone through its project structure to understand how the framework plugs in
required settings through the build file. We have also discussed the various bits and
pieces of such applications: models, routes, views, controllers, and so on.
In the next chapter, we will cover actions in detail.
[ 16 ]
www.PacktPub.com
Stay Connected: