Find file
Fetching contributors…
Cannot retrieve contributors at this time
541 lines (447 sloc) 23.7 KB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Intro to Play Framework</title>
<meta name="description" content="Play Framework is the High Velocity Web Framework For Java and Scala. It is lightweight, stateless, RESTful, and developer friendly. This is an introduction to building web applications with Play. You will learn about: routing, Scala controllers & templates, database access, asset compilation for LESS & CoffeeScript, and JSON services.">
<meta name="author" content="James Ward">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="css/reveal.min.css">
<link rel="stylesheet" href="css/theme/default.css" id="theme">
<!-- For syntax highlighting -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- If the query includes 'print-pdf', use the PDF print sheet -->
<script>
document.write( '<link rel="stylesheet" href="css/print/' + ( window.location.search.match( /print-pdf/gi ) ? 'pdf' : 'paper' ) + '.css" type="text/css" media="print">' );
</script>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h2>Intro to Play Framework</h2>
<h3>Java Edition</h3>
<p>
<small><a href="http://www.jamesward.com">James Ward</a> ~ <a href="http://twitter.com/_jamesward">@_JamesWard</a></small>
</p>
</section>
<section data-markdown>
<script type="text/template">
## Play Framework
### The High Velocity Web Framework for Java & Scala
* Just hit refresh workflow
* Type safety
* RESTful by default
* Stateless
* Reactive
* Asset Compiler
* First-class JSON
</script>
</section>
<section data-markdown>
<script type="text/template">
## Get Started
#### Typesafe Activator - Reactive Development Tool for Play
[typesafe.com/platform/getstarted](http://typesafe.com/platform/getstarted)
* 50+ Templates with Tutorials
* Web UI and CLI
* Edit Code in Browser, Eclipse, IntelliJ IDEA, or other editor
* Run App & Tests via Browser
* Typesafe Console Integration
</script>
</section>
<section>
<p>Create a New Play App</p>
<pre><code>activator new</code></pre>
<p>IDE Support</p>
<pre><code>activator idea</code></pre>
<p>Run the App</p>
<pre><code>activator ~run</code></pre>
<p>Open the App</p>
<a href="http://localhost:9000">http://localhost:9000</a>
</section>
<section data-markdown>
<script type="text/template">
## Routes
### Declarative, Type-safe URL Mapping
VERB PATH CONTROLLER_METHOD
GET / controllers.Application.index()
GET /foo controllers.Application.foo()
</script>
</section>
<section data-markdown>
<script type="text/template">
## Controllers
### Receive a Request, Return a Response
public static Result index() {
return ok(index.render("hello, world"));
}
</script>
</section>
<section data-markdown>
<script type="text/template">
## Views
### Composable, Type-safe Scala Templates
@(message: String)
@main("Welcome to Play 2.0") {
@message
}
</script>
</section>
<section>
<section>
<h2>Testing</h2>
<ul>
<li>Based on JUnit</li>
<li>Unit Tests with Mocks</li>
<li>Functional Tests for Templates, Controllers, Router</li>
</ul>
<p>Run All Tests Once</p>
<pre><code>activator test</code></pre>
<p>Run All Tests Continuously</p>
<pre><code>activator ~test</code></pre>
<p>Run One Test</p>
<pre><code>activator "test-only my.namespace.MySpec"</code></pre>
<p>Run Failed Tests</p>
<pre><code>activator test-quick</code></pre>
</section>
<section data-markdown>
<script type="text/template">
### Test a Template
@Test
public void indexTemplate() {
Content html = views.html.index.render("test");
assertThat(contentType(html)).isEqualTo("text/html");
assertThat(contentAsString(html)).contains("test");
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Test a Controller
@Test
public void callIndex() {
running(fakeApplication(), new Runnable() {
public void run() {
Result result = callAction(controllers.routes.ref.Application.index());
assertThat(status(result)).isEqualTo(OK);
assertThat(contentType(result)).isEqualTo("text/html");
assertThat(charset(result)).isEqualTo("utf-8");
assertThat(contentAsString(result)).contains("Your new application is ready.");
}
});
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Test a Route
@Test
public void fooRoute() {
running(fakeApplication(), new Runnable() {
public void run() {
Result result = route(fakeRequest("GET", "/foo"));
assertThat(result).isNotNull();
}
});
}
</script>
</section>
</section>
<section>
<section>
<h2>Models</h2>
<br/>
<ul>
<li>EBean for Java</li>
<li>JPA Annotations</li>
<li>Versioned Evolution Scripts</li>
</ul>
</section>
<section data-markdown>
<script type="text/template">
### conf/application.conf
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
ebean.default="models.*"
</script>
</section>
<section data-markdown>
<script type="text/template">
### app/models/Bar.java
package models;
import play.db.ebean.Model;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Bar extends Model {
@Id
public String id;
public String name;
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Test a Model
@Test
public void create() {
running(fakeApplication(), new Runnable() {
public void run() {
Bar bar = new Bar();
bar.name = "Write a test";
bar.save();
assertThat(bar.id).isNotNull();
}
});
}
</script>
</section>
</section>
<section>
<section>
<h2>Forms</h2>
<br/>
<ul>
<li>Controller: Bind request data to VO</li>
<li>View: Server-side Form Helpers</li>
</ul>
</section>
<section data-markdown>
<script type="text/template">
### Controller
public static Result addBar() {
Bar bar = Form.form(Bar.class).bindFromRequest().get();
bar.save();
return redirect(routes.Application.index());
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Route
POST /bars controllers.Application.addBar()
</script>
</section>
<section data-markdown>
<script type="text/template">
### View
@helper.form(action = routes.Application.addBar()) {
<input name="name"/>
<input type="submit"/>
}
</script>
</section>
</section>
<section data-markdown>
<script type="text/template">
## JSON - Jackson Wrappers
#### Controller
public static Result getBars() {
List<Bar> bars = new Model.Finder(String.class, Bar.class).all();
return ok(toJson(bars));
}
#### Route
GET /bars controllers.Application.getBars()
</script>
</section>
<section data-markdown>
<script type="text/template">
## Form Validation
#### Model
@Constraints.Required
public String name;
#### Controller
public static Result addBar() {
Form<Bar> form = form(Bar.class).bindFromRequest();
if (form.hasErrors()) {
return badRequest(index.render(form));
}
else {
// save
}
}
</script>
</section>
<section>
<section>
<h2>Asset Compilers</h2>
<h3>Unified Client/Server Development</h3>
<ul>
<li>assets/javascripts/foo.{js|coffee}</li>
<li>assets/stylesheets/foo.less</li>
</ul>
<pre><code>&lt;script src='@routes.Assets.at("javascripts/foo.min.js")'&gt;&lt;/script&gt;</code></pre>
</section>
<section data-markdown>
<script type="text/template">
### CoffeeScript
#### app/assets/javascripts/index.coffee
$ ->
$.get "/bars", (data) ->
$.each data, (index, bar) ->
$("#bars").append $("<li>").text bar.name
#### Auto-Minification
<script src='@routes.Assets.at("javascripts/index.min.js")'>
</script>
</section>
</section>
<section>
<section data-markdown>
<script type="text/template">
## WebJars
### The Web in Jars
* Common client-side libraries: jQuery, Bootstrap, etc
* Manage versioning through dependency management
* Transitive dependencies
* RequireJS support
* CDN & Cache Friendly
</script>
</section>
<section>
Specify Dependencies in <code>build.sbt</code>
<pre><code>"org.webjars" %% "webjars-play" % "2.2.0",
"org.webjars" % "bootstrap" % "2.3.1"</code></pre>
Create Route for <code>WebJarAssets</code> Controller
<pre><code>GET /webjars/*file controllers.WebJarAssets.at(file)</code></pre>
Use a WebJar
<pre><code>&lt;script src='@routes.WebJarAssets.at(WebJarAssets.locate("jquery.min.js"))'&gt;&lt;/script&gt;</code></pre>
</section>
</section>
<section>
<section>
<h2>Reactive Requests</h2>
<img src="assets/reactive-requests.png">
</section>
<section data-markdown>
<script type="text/template">
### Blocking Request
public static Result index() {
return ok(views.html.index.render("hello"));
}
### Async Request
public static F.Promise<Result> index() {
return F.Promise.promise(new F.Function0<Result>() {
public Result apply() {
return ok(views.html.index.render("hello"));
}
});
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Reactive Request (Async + Non-Blocking)
public static F.Promise<Result> index() {
return F.Promise.delayed(new F.Function0<Result>() {
public Result apply() throws Throwable {
return ok(views.html.index.render("hello"));
}
}, 5, TimeUnit.SECONDS);
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Reactive Requests
public static F.Promise<Result> index() {
F.Promise<WS.Response> jw = WS.url("http://www.jamesward.com").get();
return jw.map(new F.Function<WS.Response, Result>() {
public Result apply(WS.Response response) throws Throwable {
return ok(response.getBody());
}
});
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Reactive Composition
public static F.Promise<Result> index() {
final F.Promise<WS.Response> jw = WS.url("http://www.jamesward.com").get();
final F.Promise<WS.Response> tw = WS.url("http://www.twitter.com").get();
return jw.flatMap(new F.Function<WS.Response, F.Promise<Result>>() {
public F.Promise<Result> apply(final WS.Response jwR) throws Throwable {
return tw.map(new F.Function<WS.Response, Result>() {
public Result apply(WS.Response twR) throws Throwable {
return ok(twR.getBody() + jwR.getBody());
}
});
}
});
}
</script>
</section>
<section data-markdown>
<script type="text/template">
### Reactive Push with SSE
public static Result events() {
EventSource eventSource = new EventSource() {
public void onConnected() {
sendData("hello");
}
};
return ok(eventSource);
}
$ ->
events = new EventSource("/events")
events.onmessage = (e) ->
console.log(e.data)
</script>
</section>
<section data-markdown>
<script type="text/template">
### 2-Way Reactive with WebSockets
public static WebSocket<String> echo() {
return new WebSocket<String>() {
public void onReady(final In<String> in, final Out<String> out) {
in.onMessage(new F.Callback<String>() {
public void invoke(String message) throws Throwable {
out.write(message);
}
});
}
};
}
$ ->
ws = new WebSocket("ws://localhost:9000/echo")
ws.onopen = () ->
ws.send("foo")
ws.onmessage = (message) ->
console.log(message.data)
</script>
</section>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.min.js"></script>
<script>
// Full list of configuration options available here:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
transition: Reveal.getQueryHash().transition || 'default', // default/cube/page/concave/zoom/linear/fade/none
// Optional libraries used to extend on reveal.js
dependencies: [
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
]
});
</script>
</body>
</html>