How to demo the Play framework - live coding script (in Scala)

09 August 2010

Francisco Canedo

by Francisco Canedo

If you want someone to know how fast and straightforward it is to build a Scala web application with the Play framework, then show, don’t tell. The most compelling way to do this is to download Play and start coding a web application from scratch, while they watch. This article is a script for a five-minute live-coding Play demo.

This script is based on Peter Hilton’s demo, but with Scala. See his article for more information.

Summary

  1. Download the Play framework.

  2. Download the Scala plugin.

  3. Create and run the application.

  4. Inspect the set-up for the default welcome page.

  5. Replace the welcome page with a trivial dynamic template.

  6. Generate a compilation error.

  7. Retrieve HTTP request parameters.

  8. Rendering binary data.

  9. Show message file content in the template

  10. Add the project to Eclipse.

  11. Add a JPA Entity.

  12. Add an HTML form for creating entity instances.

  13. Add a link for deleting each instance.

  14. Use extensions in the view template.

  15. Add form validation.

Create and run the application

Open http://download.playframework.org/1.1-nightly/ Download one of the nightly builds for 1.1, unless the final version has already been released — I used 1.1-unstable-r942. Resist the urge to use any other version for use with this demo.

There is no need to install Play as such, although you might want to add the download’s directory to your PATH, so you can execute the $PLAY_HOME/play commmand directly. On my machine, though, it’s just as easy to just type ~/Downloads/play-1.1-unstable-r942/play for this kind of demo.

Execute play install scala

Execute play new tasks --with scala~ What is the application name? tasks

A task list application is an easy example for people to understand, but it is more fun to pick an example that is more familiar and relevant to your audience, such as 'customers' or 'cocktails'.

Execute find tasks (optional) - List the generated files

It is crucial to note that Play has not generated a lot of code here; the files are minimal stubs that you will only add to, rather than a lot of generated code that you will have to delete.

Execute play run tasks - Start the Play server runtime to run the application.

Depending on what you are used to, start-up time may seem extremely short.

Open http://localhost:9000/ - Show welcome page.

The welcome page describes how the stub application works, which the next few steps show.

Start a text editor.

You could use an IDE, such as Eclipse, already at this point. However, it is useful to start with a plain text editor to make it clear that you do not have to compile the Scala code yourself.

If you are doing this demo with TextMate on a Mac, you can uncomment the line that starts with # play.editor. This will allow you to click on the sources in Play’s error messages to make TextMate jump to the right place in the file.

Edit conf/routes - Show the route for the GET / HTTP request.

Since Play has an HTTP focus, an incoming HTTP request is a good starting point for explaining how it works. The routes file, is therefore crucial, because this defines the (two-way) mapping between HTTP requests and Scala methods.

Edit app/controllers/Application.scala - Show the index method.

Edit app/views/main.html - Show the default template: HTML 5, CSS and jQuery.

Edit app/views/Application/index.html - Show the #{welcome /} tag and replace it with <h1>Tasks</h1>.

Open http://localhost:9000/ - Show the heading.

Show dynamic data in the template

Edit app/views/Application/index.html - Change the heading to <h1>${items}</h1>.

Edit app/controllers/Application.scala - Change the index method to the following (omitting a parenthesis).

\{% highlight scala %} def index \{ val items = "Things" render(items } \{% endhighlight %}

Open http://localhost:9000/ - Show the Scala compilation error.

Edit app/controllers/Application.scala - Add the missing parenthesis.

Open http://localhost:9000/ - Show the Scala changes without compilation or deployment.

Edit app/controllers/Application.scala - Replace the items declaration line with an items: String = "default" method parameter.

Open http://localhost:9000/?items=Things and http://localhost:9000/ to show the working default value.

Edit app/controllers/Application.scala - Undo the last change - remove the parameter and put the declaration back.

Rendering binary data

Edit app/controllers/Application.scala - Add a new method.

\{% highlight scala %} import play.libs.Images … def captcha = Images.captcha \{% endhighlight %}

Edit conf/routes - Add GET /captcha Application.captcha

Open http://localhost:9000/captcha - Show the rendered image, show that the image has the correct mime-type according to the browser.

Images.captcha, which is part of Play’s built-in captcha facilities, renders a new captcha image for every call.

Show message file content in the template

Edit app/views/Application/index.html - Change the heading to <h1>&{'model.tasks'}</h1>.

Open http://localhost:9000/ - Show the message key being displayed, because the message is not defined.

Edit conf/messages - Add the line model.tasks = Tasks

Open http://localhost:9000/ - Show the message being displayed.

Eclipse

Execute Control+C - Show how little logging there is by default.

Execute play eclipsify tasks - Generate Eclipse project and class path configuration.

Eclipse File » Import… » Existing projects into workspace - Show project structure.

Eclipse eclipse/tasks.launch » Run » tasks - Start the Play server runtime from within Eclipse.

Open http://localhost:9000/ - Show the application running.

JPA entity

Edit app/models - create class:

package models

import javax.persistence.Entity

import play.db.jpa.Model
import play.db.jpa.QueryOn

@Entity
class Task(
   var title: String
) extends Model {
   override def toString = title
}

object Task extends QueryOn[Task]

Edit app/controllers/Application.scala - Change the index method to

import models.Task
…
def index {
   val tasks = Task.findAll
   render(tasks)
}

Edit app/views/Application/index.html - After the heading, add:

<ul>
#{list tasks, as:'task'}
   <li>${task.title}</li>
#{/list}
</ul>

Open http://localhost:9000/ - Show the JPA error.

Edit conf/application.conf - Uncomment the line # db=mem

Play needs to be restarted for this to take effect. Press Ctrl+C in the terminal window running Play and rerun the command play run tasks

Open http://localhost:9000/ - Show the page - no tasks.

HTML form

Edit app/views/Application/index.html - After the list, add:

#{form @add()}
<p><input name="task.title"></p> <p><input type="submit" value="Add Task"></p>
#{/form}

Edit app/controllers/Application.scala - Add the method:

def add(task: Task) {
   task.save
   index
}

Open http://localhost:9000/ - Add tasks.

Edit app/views/Application/index.html - Inside the <li> add a link:

<a href="@{delete(task.id)}">delete</a>

As for forms, there is also a tag for generating links; this way just generates the URL.

Edit conf/routes - Add GET /delete Application.delete

Edit app/controllers/Application.scala - Add the method, noting the id parameter:

def delete(id: Long) {
   Task.findById(id).foreach(_.delete())
   index
}

The foreach idiom is because findById returns a Scala Option which can behave like an empty list or one filled with one entry. This means that you don’t have to check for null here, the call to _.delete is just never run if no task was found.

Open http://localhost:9000/ - Delete tasks - show the link URL and query string parameter.

Extensions

Edit app/views/Application/index.html - Change the heading to:

<h1>${tasks.size()} Task${tasks.pluralize()}</h1>

Open http://localhost:9000/ - Add/delete tasks to show singular and plural forms.

If you are lucky, at this point someone in the audience will be smart enough to point out that some plurals are not just formed by adding an 's', at which point you can change the example, and show the pluralize method with one or more parameters, e.g. ${tasks.pluralize(messages.get('task'), messages.get('tasks'))}

Form validation

Edit models/Task.scala - Add the @Required (import play.data.Validators.Required) annotation to the title field.

Edit app/controllers/Application.scala - Add the @Valid annotation to the add method’s Task parameter, replace the first line of the method body (Task.save) with the following.

import play.data.validation._
…
if (Validation.hasErrors) {
   Validation.keep
}
else {
   task.save
}

Edit app/views/Application/index.html - before the form, add:

#{errors}
    <p style="color:red">${error}</p>
#{/errors}

Open http://localhost:9000/ - Show the validation error when submitting an empty name.

The validation error is just 'Required', but we can change this.

Edit conf/messages - Add the line validation.required = %s is a required field

Open http://localhost:9000/ - Show the new validation error.

Now we get the field name, but not as a formatted label.

Edit conf/messages - Add the line task.title = Task name

Open http://localhost:9000/ - Show the new validation error.

This lists validation errors in one place. A better way is to list the errors next to each field.

Edit app/views/Application/index.html - Replace the errors tag with:

#{ifErrors}
    <p style="color:red">Validation failed</p>
#{/ifErrors}

… and after the text input, before the closing </p> tag, add:

<strong style="color:red">#{error 'task.title' /}</strong>

Open http://localhost:9000/ - Show the new validation error.