Play 2.0 demo - live coding script
Play 2.0 demo - live coding script
--- layout: article drupal-format: Unfiltered HTML, with syntax highlighter title: "Play 2.0 demo - live coding script" tags: playframework author: Peter Hilton summary: "The best way to introduce Play to an audience is to show them how it works in the kind of live-coding demo that lets people see for themselves how easy it is. Here is a Play 2.0 (Java) version of last year’s [Play 1.x live coding script]( https://blog.lunatech.com/posts/2010-06-14-how-demo-play-framework-live-coding-script), in which you will code a simple but functional to-do list application, using Play 2.0, Java and Ebean.
Getting started
Download Play 2.0. This version of the script is based on the 2.0-betarelease.
$ wget http://download.playframework.org/releases/play-2.0-beta.zip
$ unzip -q play-2.0-beta.zip
$ export PATH=$PATH:`pwd`/play-2.0-beta
*Note: at this point you would normally add the play command to the path more permanently.*
Create the application:
$ play new tasks
What is the application name?
> tasks
Which template do you want to use for this new application?
1 - Create a simple Scala application
2 - Create a simple Java application
3 - Create an empty project
> 2
See which files were generated:
$ find tasks -type f
tasks/.gitignore
tasks/app/controllers/Application.java
tasks/app/views/index.scala.html
tasks/app/views/main.scala.html
tasks/conf/application.conf
tasks/conf/routes
tasks/project/build.properties
tasks/project/Build.scala
tasks/project/plugins.sbt
tasks/public/images/favicon.png
tasks/public/javascripts/jquery-1.6.4.min.js
tasks/public/stylesheets/main.css
Run the application:
$ cd tasks
$ play run
Open the welcome page: http://localhost:9000/
Compilation errors and dynamic data
Edit app/views/index.scala.html and replace the contents with the following (with a bogus variable name):
@(items: String)
@main("Tasks") {
<h1>@item</h1>
}
Reload the page, which shows a template compilation error:not found: value item.
In the console, type Control-D to stop the application, then start the Play console and compile the application:
$ play
[tasks] $ compile
*You can also discover template compilation errors without running the application.*
Start the application again:
[tasks] $ run
In app/views/index.scala.html fix the error: change @item to@items.
Edit app/controllers/Application.java and change the index() method body to the following line (with a missing semi-colon):
return ok(index.render("Things"))
Reload the page, which shows a Java compilation error: ';' expected.
In app/controllers/Application.java fix the error: add the missing semi-colon.
Reload the page, which shows the heading ‘Things’.
In public/stylesheets/main.css, add some CSS to make things less ugly:
body { font-family:"Helvetica Neue"; padding:2em; background: #B2EB5A url("/assets/images/play20header.png") no-repeat top center ; }
body:before { content:'Play 2.0 task list demo'; color:rgba(255,255,255,0.7); font-size:150%; text-transform:uppercase; letter-spacing:0.4em; }
ul { padding:0; list-style:none; }
li, form { width:30em; background:white; padding:1em; border:1px solid #ccc; border-radius:0.5em; margin:1em 0; position:relative; }
li a { text-decoration:none; color:transparent; position:absolute; top:1em; right:1em; }
li a:after { content:'❎'; color:#aaa; font-size:120%; font-weight:bold; }
form * { font-size:120%; }
input { width:16em; }
button { cursor:pointer; color: white; background-color: #3D6F04; background-image: -webkit-linear-gradient(top, #5AA706, #3D6F04); text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); border: 1px solid #CCC; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); border-radius:4px; }
p.error { margin:0; color:#c00; }
In app/controllers/Application.java replace "Things" with aString items method parameter.
public static Result index(final String items) {
return ok(index.render(items));
}
In conf/routes, replace the first route with (with a bogus lower-casestring type):
GET / controllers.Application.index(i: string)
Open http://localhost:9000/?i=Tasks,
which shows a routes compilation error: not found: type string.
*The routes file is compiled, and HTTP parameters must be declared. HTTP parameter names do not have to match the action method names.*
In conf/routes, correct the error: change the parameter type toString.
Reload the web page.
Undo the last changes in app/controllers/Application.java – remove theindex method parameter:
public static Result index() {
return ok(index.render("Tasks"));
}
Undo the change in conf/routes – remove the method parameter:
GET / controllers.Application.index()
Set-up IntelliJ IDEA
In the console, type Control-D to stop the application, and create the IntelliJ project:
[tasks] $ gen-idea
*This currently uses a separate sbt plug-in, but something like this will be built in to Play 2.0.*
Open the project, containing the generated .idea directory, in IntelliJ IDEA.
Ebean entity
Create a new models.Task class in app/models/Task.java, either by hand or using IntelliJ IDEA:
package models;
import play.db.ebean.Model;
import javax.persistence.Id;
import javax.persistence.Entity;
/**
* A human-task, e.g. 'Get the presenter a beer'.
*/
@Entity
public class Task extends Model {
@Id
public Long id;
public String title;
public static Finder<Long, Task> find = new Finder<Long, Task>(Long.class, Task.class);
}
*The ‘finder’ is more explicit than the methods added by byte code enhancement in Play 1.*
In app/controllers/Application.java, import models.Task and replace"Things" with a call to the finder:
return ok(index.render(Task.find.orderBy("title").findList()));
Open http://localhost:9000/,
which shows a Java compilation error:render(java.lang.String) in views.html.index cannot be applied to (java.util.List<models.Task>).
In app/views/index.scala.html change the type in the template parameter declaration:
@(tasks: List[models.Task])
Also, change the HTML block to:
<h1>@tasks.size task@(if(tasks.size != 1) "s")</h1>
<ul>
@for(task <- tasks) {
<li>@task.title</li>
}
</ul>
Reload the page, which shows a data source error:[RuntimeException: DataSource user is null?].
In conf/application.conf, uncomment the default values for the in-memory database and Ebean configuration:
db.default.driver=org.h2.Driver
db.default.url=jdbc:h2:mem:play
ebean.default=models.*
Reload the web page, which shows the ‘Database ‘default’ needs evolution!’ page.
*The db/evolutions/default/1.sql database evolution script is generated for you.*
Click the ‘Apply this script now!’ button.
*In templates, dynamic content and control structures start at @ and continue until the end of the statement or expression.*
Open http://localhost:9000/: there are no tasks.
HTML form
In app/views/index.scala.html, add a form after the list.
<form method="post" action="@routes.Application.add()">
<input name="title" placeholder="Enter a task description…">
<button type="submit">Add Task</button>
</form>
In app/controllers/Application.java, import play.data.Form and add the add method:
public static Result add() {
final Form<Task> taskForm = form(Task.class).bindFromRequest();
final Task task = taskForm.get();
task.save();
return redirect(routes.Application.index());
}
In conf/routes, add the new HTTP mapping:
POST / controllers.Application.add()
Reload http://localhost:9000/, enter a value in the text input and click the Add button.
Command link
In app/views/index.scala.html, inside the <li>, add a link:
<a href="@routes.Application.delete(task.id)">delete</a>
In conf/routes, add the new HTTP mapping:
GET /delete/:id controllers.Application.delete(id: Long)
In app/controllers/Application.java, add the delete method:
public static Result delete(final Long id) {
Task.find.ref(id).delete();
return redirect(routes.Application.index());
}
Reload http://localhost:9000/ and delete some tasks, showing the link URLs.
Form validation
In app/models/Task.java, import play.data.validation.Constraints and annotate the title field with @Constraints.Required.
@Constraints.Required
public String title;
In app/controllers/Application.java, import java.util.List and extract the list of tasks to a new method:
private static List<Task> tasks() {
return Task.find.orderBy("title").findList();
}
Add validation to the add method:
public static Result add() {
final Form<Task> taskForm = form(Task.class).bindFromRequest();
if(taskForm.hasErrors()) {
return badRequest(index.render(tasks(), taskForm));
} else {
taskForm.get().save();
return index();
}
}
*The play.data.Form is an explicit wrapper for HTTP form data and validation errors, used for binding. This is more explicit and more structured than the Play 1.2 validation data.*
Change the index method to add an empty form to the template call:
return ok(index.render(tasks(), form(Task.class)));
In app/views/index.scala.html, add the new form parameter on the first line:
@(tasks: List[models.Task], form:play.data.Form[models.Task])
After the form’s submit button, add a line to display error messages:
<p class="error">@form("title").errors.map(_.message).map(Messages(_))</p>
*The play.api.i18n.Messages Scala object is being used for message key look-up.*