play vs. grails smackdown - raible...
TRANSCRIPT
![Page 1: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/1.jpg)
PLAY VS. GRAILSSMACKDOWNJAMES WARD AND MATT RAIBLE
and
Changelog
March 24, 2013: Updated and for .
June 24, 2012: .
June 19, 2012: Published for .
@_JamesWard @mraible
statistics load tests Devoxx France
Play Performance Fix
ÜberConf
![Page 2: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/2.jpg)
WHY A SMACKDOWN?Play 2 and Grails 2 are often hyped as the most productive
JVM Web Frameworks.
* We wanted to know how they enhanced the Developer Experience (DX).
![Page 3: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/3.jpg)
HAPPY TRAILS REQUIREMENTSServer-side TemplatesPlay 2 with JavaForm ValidationData PaginationAuthenticationScheduled Jobs
Atom / RSSEmail NotificationsUnit / Integration TestsLoad TestsPerformance Tests
Stretch Goals: Search, Photo Upload to S3
![Page 4: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/4.jpg)
OUR SCHEDULEWeek 1 - Data Model DefinitionWeek 2 - Data Layer & URL DesignWeek 3 - Controllers & AuthWeek 4 - ViewsWeek 5 - Misc Polish
![Page 5: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/5.jpg)
INTRO TO PLAY 2
![Page 6: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/6.jpg)
“Play is based on a lightweight, stateless, web-friendly architecture and features predictable
and minimal resource consumption (CPU,memory, threads) for highly-scalable
applications - thanks to its reactive model,based on Iteratee IO.”
![Page 7: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/7.jpg)
MY TOP 10 FAVORITE FEATURES1. Simple2. URL Routing3. Class Reloading4. Share-Nothing5. Java & Scala Support6. Great Testing Support7. JPA/EBean Support8. NIO Server (Netty)9. Asset Compiler
10. Instant Deployment on Heroku
![Page 8: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/8.jpg)
INTRO TO GRAILS 2
![Page 9: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/9.jpg)
“ Powered by Spring, Grails outperforms thecompetition. Dynamic, agile web development
without compromises. ”
![Page 10: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/10.jpg)
MY TOP 10 FAVORITE FEATURES1. Documentation2. Clean URLs3. GORM4. IntelliJ IDEA Support5. Zero Turnaround6. Excellent Testing Support7. Groovy8. GSPs9. Resource Optimizer
10. Instant Deployment on Heroku
![Page 11: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/11.jpg)
OUR SETUPIntelliJ IDEA for DevelopmentGitHub for Source ControlCloudBees for Continuous IntegrationHeroku for Production
Later added: QA Person and BrowserMob
![Page 12: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/12.jpg)
CODE WALK THROUGHWe developed the same app, in similar ways, so let's look at
the different layers.
↓
DatabaseURL MappingModelsControllersViewsValidationIDE Support
JobFeedEmailPhoto UploadTestingDemo DataConfigurationAuthentication
![Page 13: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/13.jpg)
DATABASE - GRAILSHibernate is the default persistence providerCreate models, Hibernate creates the schema for you
grails-app/conf/DataSource.groovy
environments { development { dataSource { dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', '' url = "jdbc:postgresql://localhost:5432/happytrails" } }
![Page 14: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/14.jpg)
DATABASE - PLAYEBean is the default persistence provider in Java projectsEvolutions can be auto-appliedInitial evolution sql is auto-createdSubsequent changes must be versionedAuto-created schema file is database dependentPlay 2 supports multiple datasources (Play 1 does not)
conf/evolutions/default/2.sql
# --- !Ups
ALTER TABLE account ADD is_admin boolean;
UPDATE account SET is_admin = FALSE;
# --- !Downs
ALTER TABLE account DROP is_admin;
![Page 15: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/15.jpg)
DATABASE - PLAYUsing Heroku Postgres in production rocks!
Postgres 9.1DataclipsFork & FollowMulti-Ingres
![Page 16: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/16.jpg)
URL MAPPING - GRAILSgrails-app/conf/UrlMappings.groovy
class UrlMappings { static mappings = { "/$controller/$action?/$id?" { constraints { // apply constraints here } } "/"(controller: "home", action: "index") "/login"(controller: "login", action: "auth") "/login/authfail"(controller: "login", action: "authfail") "/login/denied"(controller: "login", action: "denied") "/logout"(controller: "logout") "/signup"(controller: "register") "/feed/$region"(controller: "region", action: "feed") "/register/register"(controller: "register", action: "register") "/register/resetPassword"(controller: "register", action: "resetPassword") "/register/verifyRegistration"(controller: "register", acti
![Page 17: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/17.jpg)
URL MAPPING - PLAYconf/routes
GET / controllers.ApplicationController.index()
GET /signup controllers.ApplicationController.signupForm()POST /signup controllers.ApplicationController.signup()
GET /login controllers.ApplicationController.loginForm()POST /login controllers.ApplicationController.login()
GET /logout controllers.ApplicationController.logout()
GET /addregion controllers.RegionController.addRegion()POST /addregion controllers.RegionController.saveRegion()
GET /:region/feed controllers.RegionController.getRegionFeed(region)
GET /:region/subscribe controllers.RegionController.subscribe(region)GET /:region/unsubscribe controllers.RegionController.unsubscribe(region)
GET /:region/addroute controllers.RegionController.addRoute(region)POST /:region/addroute controllers.RegionController.saveRoute(region)
GET /:region/delete controllers.RegionController.deleteRegion(region)
GET /:region/:route/rate controllers.RouteController.saveRating(region, route, rating: java.lang.Integer)
POST /:region/:route/comment controllers.RouteController.saveComment(region, route)
GET /:region/:route/delete controllers.RouteController.deleteRoute(region, route)
GET /:region/:route controllers.RouteController.getRouteHtml(region, route)
GET /:region controllers.RegionController.getRegionHtml(region, sort ?= "name")
![Page 18: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/18.jpg)
MODELS - GRAILSAll properties are persisted by defaultConstraintsMappings with hasMany and belongsToOverride methods for lifecycle events
grails-app/domain/happytrails/Region.groovy
package happytrails
class Region { static charactersNumbersAndSpaces = /[a-zA-Z0-9 ]+/ static searchable = true
static constraints = { name blank: false, unique: true, matches: charactersNumbersAndSpaces seoName nullable: true routes cascade:"all-delete-orphan" }
static hasMany = [ routes:Route ]
![Page 19: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/19.jpg)
MODELS - PLAYEBean + JPA AnnotationsDeclarative Validations (JSR 303)Query DSLLazy Loading (except in Scala Templates)
app/models/Direction.java
@Entitypublic class Direction extends Model {
@Id public Long id;
@Column(nullable = false) @Constraints.Required public Integer stepNumber;
@Column(length = 1024, nullable = false) @Constraints.MaxLength(1024) @Constraints.Required public String instruction;
![Page 20: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/20.jpg)
CONTROLLERS - GRAILSgrails-app/controllers/happytrails/HomeController.groovy
package happytrails
import org.grails.comments.Comment
class HomeController {
def index() { params.max = Math.min(params.max ? params.int('max') : 10, 100) [regions: Region.list(params), total: Region.count(), comments: Comment.list(max: 10, sort: 'dateCreated', order: 'desc')] }}
![Page 21: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/21.jpg)
CONTROLLERS - PLAYStateless - Composable - InterceptableClean connection between URLs and response code
app/controllers/RouteController.java
@With(CurrentUser.class)public class RouteController extends Controller {
@Security.Authenticated(Secured.class) public static Result saveRating(String urlFriendlyRegionName, String urlFriendlyRouteName, Integer rating) { User user = CurrentUser.get(); Route route = getRoute(urlFriendlyRegionName, urlFriendlyRouteName); if ((route == null) || (user == null)) { return badRequest("User or Route not found"); }
if (rating != null) { Rating existingRating = Rating.findByUserAndRoute(user, route); if (existingRating != null) { existingRating.value = rating; existingRating.save(); } else { Rating newRating = new Rating(user, route, rating); newRating.save(); } } return redirect(routes.RouteController.getRouteHtml(urlFriendlyRegionName, urlFriendlyRouteName)); }
![Page 22: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/22.jpg)
VIEWS - GRAILSGroovy Server Pages, like JSPsGSP TagsLayouts and TemplatesOptimized Resources
grails-app/views/region/show.gsp
<%@ page import="happytrails.Region" %><!doctype html><html><head> <meta name="layout" content="main"> <g:set var="entityName" value="${message(code: 'region.label', default: 'Region')}"/> <title><g:message code="default.show.label" args="[entityName]"/></title> <link rel="alternate" type="application/atom+xml" title="${regionInstance.name} Updates" href="${createLink(controller: 'region', action: 'feed', params: [region: regionInstance.seoName])}"/></head>
![Page 23: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/23.jpg)
VIEWS - PLAYScala TemplatesComposableCompiled
app/views/region.scala.html
@(region: Region, sort: String)
@headContent = { <link rel="alternate" type="application/rss+xml" title="@region.getName RSS Feed" href="@routes.RegionController.getRegionFeed(region.getUrlFriendlyName)" />}
@breadcrumbs = { <div class="nav-collapse"> <ul class="nav"> <li><a href="@routes.ApplicationController.index()">Hike</a></li> <li class="active"><a href="@routes.RegionController.getRegionHtml(region.getUrlFriendlyName)">@region.getName</a></li> </ul> </div>
![Page 24: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/24.jpg)
VALIDATION - GRAILSgrails-app/controllers/happytrails/RouteController.groovy
def save() { def routeInstance = new Route(params) if (!routeInstance.save(flush: true)) { render(view: "create", model: [routeInstance: routeInstance]) return }
flash.message = message(code: 'default.created.message', args: [message(code: 'route.label', default: 'Route'), routeInstance.name]) redirect(action: "show", id: routeInstance.id) }
grails-app/views/route/create.gsp
<g:hasErrors bean="${routeInstance}"> <div class="alert alert-error" role="alert"> <g:eachError bean="${routeInstance}" var="error"> <div <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></div> </g:eachError> </div> </g:hasErrors>
![Page 25: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/25.jpg)
VALIDATION - PLAYapp/controllers/RouteController.java
public static Result signup() { Form<User> signupForm = form(User.class).bindFromRequest(); if (signupForm.hasErrors()) { return badRequest(views.html.signupForm.render(signupForm)); }
app/views/signupForm.scala.html
@if(signupForm.hasGlobalErrors) { <p class="error alert alert-error">@signupForm.globalError.message</p>}
@helper.form(action = routes.ApplicationController.signup(), 'class -> "form-horizontal") { @helper.inputText(signupForm("fullName"), '_label -> "Full Name") @helper.inputText(signupForm("emailAddress"), '_label -> "Email Address") @helper.inputPassword(signupForm("password"), '_label -> "Password") <div class="controls"> <input type="submit" class="btn btn-primary" value="Create Account"/> </div>}
![Page 26: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/26.jpg)
IDE SUPPORT - GRAILS
IntelliJ Rocks!
![Page 27: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/27.jpg)
IDE SUPPORT - PLAY
$ play idea$ play eclipsify
Java!!!Debugging Support via Remote DebuggerLimited Testing within IDE
![Page 28: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/28.jpg)
JOB - GRAILSgrails-app/jobs/happytrails/DailyRegionDigestEmailJob.groovy
package happytrails
import org.grails.comments.Comment
class DailyRegionDigestEmailJob { def mailService
static triggers = { //simple repeatInterval: 5000l // execute job once in 5 seconds cron name:'cronTrigger', startDelay:10000, cronExpression: '0 0 7 ? * MON-FRI' // 7AM Mon-Fri }
def execute() { List<RegionUserDigest> digests = getRegionUserDigests() for (digest in digests) {
![Page 29: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/29.jpg)
JOB - PLAY
Plain old `static void main`Independent of web app
app/jobs/DailyRegionDigestEmailJob.java
public class DailyRegionDigestEmailJob { public static void main(String[] args) {
Application application = new Application(new File(args[0]), DailyRegionDigestEmailJob.class.getClassLoader(), null, Mode.Prod());
Play.start(application); List<RegionUserDigest> regionUserDigests = getRegionUserDigests();
![Page 30: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/30.jpg)
FEED - GRAILS1. grails install-plugin feeds2. Add feed() method to controller
grails-app/controllers/happytrails/RegionController.groovy
def feed = { def region = Region.findBySeoName(params.region) if (!region) { response.status = 404 return }
render(feedType: "atom") { title = "Happy Trails Feed for " + region.name link = createLink(absolute: true, controller: 'region', action: 'feed', params: ['region', region.seoName]) description = "New Routes and Reviews for " + region.name region.routes.each() { route -> entry(route.name) { link = createLink(absolute: true, controller: 'route', action: 'show', id: route.id)
![Page 31: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/31.jpg)
FEED - PLAY
No direct RSS/Atom supportDependency: "rome" % "rome" % "1.0"
app/jobs/DailyRegionDigestEmailJob.java
Region region = Region.findByUrlFriendlyName(urlFriendlyRegionName);
SyndFeed feed = new SyndFeedImpl(); feed.setFeedType("rss_2.0");
feed.setTitle("Uber Tracks - " + region.getName()); feed.setLink("http://hike.ubertracks.com"); // todo: externalize URL feed.setDescription("Updates for Hike Uber Tracks - " + region.getName());
List entries = new ArrayList(); for (Route route : region.routes) { SyndEntry entry = new SyndEntryImpl();
![Page 32: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/32.jpg)
EMAIL - GRAILS
* powered by the (built-in)
grails-app/jobs/happytrails/DailyRegionDigestEmailJob.groovy
println "Sending digest email to " + digest.user.username mailService.sendMail { to digest.getUser().username subject "Updates from Ãber Tracks " + digest.regions body message }
mail plugin
![Page 33: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/33.jpg)
EMAIL - PLAYSendGrid Heroku Add-onDependency:"com.typesafe" %% "play-plugins-mailer" % "2.0.2"
app/jobs/DailyRegionDigestEmailJob.java
MailerAPI mail = play.Play.application().plugin(MailerPlugin.class).email();mail.setSubject("Uber Tracks Region Updates");mail.addRecipient(regionUserDigest.user.getEmailAddress());mail.addFrom("[email protected]");mail.send(emailContent);
conf/prod.conf
smtp.host=smtp.sendgrid.netsmtp.port=587smtp.ssl=truesmtp.user=${SENDGRID_USERNAME}smtp.password=${SENDGRID_PASSWORD}
![Page 34: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/34.jpg)
PHOTO UPLOAD - GRAILS
![Page 35: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/35.jpg)
PHOTO UPLOAD - PLAYAmazon S3 for Persistent File Storage
app/models/S3Photo.java
PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, key, inputStream, objectMetadata);putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
if (S3Blob.amazonS3 == null) { Logger.error("Cloud not save Photo because amazonS3 was null");}else { S3Blob.amazonS3.putObject(putObjectRequest);}
app/controllers/RegionController.java
Http.MultipartFormData.FilePart photoFilePart = request().body() .asMultipartFormData().getFile("photo");
![Page 36: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/36.jpg)
TESTING - GRAILSUnit Tests with @TestFor and @MockTest URL Mappings with UrlMappingsUnitTestMixinIntegration Testing with GroovyTestCaseFunctional Testing with Geb
* Grails plugins often aren't in test scope.
test/unit/happytrails/RouteTests.groovy
package happytrails
import grails.test.mixin.*
@TestFor(Route)class RouteTests {
void testConstraints() { def region = new Region(name: "Colorado") def whiteRanch = new Route(name: "White Ranch", distance: 12.0, location: "Golden, CO", region: region) mockForConstraintsTests(Route, [whiteRanch])
// validation should fail if required properties are null
![Page 37: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/37.jpg)
TESTING - PLAYStandard JUnitUnit Tests & Functional TestsFakeApplication, FakeRequest, inMemoryDatabaseTest: Controllers, Views, Routing, Real Server, Browser
test/ApplicationControllerTest.java
@Testpublic void index() { running(fakeApplication(inMemoryDatabase()), new Runnable() { public void run() { DemoData.loadDemoData(); Result result = callAction(routes.ref.ApplicationController.index()); assertThat(status(result)).isEqualTo(OK); assertThat(contentAsString(result)).contains(DemoData.CRESTED_BUTTE_COLORADO_REGION); assertThat(contentAsString(result)).contains("<li>"); } });}
![Page 38: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/38.jpg)
DEMO DATA - GRAILS
grails-app/conf/BootStrap.groovy
import happytrails.Userimport happytrails.Regionimport happytrails.Routeimport happytrails.RegionSubscriptionimport javax.servlet.http.HttpServletRequest
class BootStrap {
def init = { servletContext -> HttpServletRequest.metaClass.isXhr = {-> 'XMLHttpRequest' == delegate.getHeader('X-Requested-With') }
if (!User.count()) { User user = new User(username: "[email protected]", password: "happyhour",
![Page 39: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/39.jpg)
DEMO DATA - PLAY
app/Global.java
public void onStart(Application application) {
//Ebean.getServer(null).getAdminLogging().setDebugGeneratedSql(true);
S3Blob.initialize(application); // load the demo data in dev mode if no other data exists if (Play.isDev() && (User.find.all().size() == 0)) { DemoData.loadDemoData(); }
super.onStart(application);}
![Page 40: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/40.jpg)
CONFIGURATION - GRAILSgrails-app/conf/Config.groovy
grails.app.context = "/"grails.project.groupId = appName // change this to alter the default package name and Maven publishing destinationgrails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request formatgrails.mime.use.accept.header = falsegrails.mime.types = [html: ['text/html', 'application/xhtml+xml'], xml: ['text/xml', 'application/xml'], text: 'text/plain', js: 'text/javascript', rss: 'application/rss+xml', atom: 'application/atom+xml', css: 'text/css', csv: 'text/csv', all: '*/*', json: ['application/json', 'text/json'], form: 'application/x-www-form-urlencoded',
![Page 41: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/41.jpg)
CONFIGURATION - PLAYBased on the TypeSafe Config LibraryOverride config with Java Properties:-Dfoo=barEnvironment Variable substitutionRun with different config files:-Dconfig.file=conf/prod.conf
conf/prod.conf
include "application.conf"
application.secret=${APPLICATION_SECRET}
db.default.driver=org.postgresql.Driverdb.default.url=${DATABASE_URL}applyEvolutions.default=true
![Page 42: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/42.jpg)
AUTHENTICATION - GRAILSSpring Security UI Plugin, “I love you!”
grails-app/conf/Config.groovy
grails.mail.default.from = "Bike Ãber Tracks <[email protected]>"
grails.plugins.springsecurity.ui.register.emailFrom = grails.mail.default.fromgrails.plugins.springsecurity.ui.register.emailSubject = 'Welcome to Ãber Tracks!'grails.plugins.springsecurity.ui.forgotPassword.emailFrom = grails.mail.default.fromgrails.plugins.springsecurity.ui.forgotPassword.emailSubject = 'Password Reset'
grails.plugins.springsecurity.controllerAnnotations.staticRules = [ '/user/**': ['ROLE_ADMIN'], '/role/**': ['ROLE_ADMIN'], '/registrationCode/**': ['ROLE_ADMIN'], '/securityInfo/**': ['ROLE_ADMIN']]
![Page 43: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/43.jpg)
AUTHENTICATION - PLAYUses cookies to remain stateless
app/controllers/Secured.java
public class Secured extends Security.Authenticator {
@Overridepublic String getUsername(Context ctx) { // todo: need to make sure the user is valid, not just the token return ctx.session().get("token");}
app/controllers/RegionController.java
@Security.Authenticated(Secured.class)public static Result addRegion() { return ok(views.html.regionForm.render(form(Region.class)));}
![Page 44: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/44.jpg)
APPLICATION COMPARISONYSlowPageSpeedLines of CodeLoad TestingWhich Loads Faster?Security Testing
![Page 45: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/45.jpg)
YSLOW
![Page 46: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/46.jpg)
![Page 47: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/47.jpg)
PAGESPEED
![Page 48: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/48.jpg)
![Page 49: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/49.jpg)
LINES OF CODE
![Page 50: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/50.jpg)
![Page 51: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/51.jpg)
LOAD TESTING WITH BROWSERMOB
![Page 52: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/52.jpg)
Bike (Grails) Hike (Play)
LOAD TESTING - 1 DYNO
![Page 53: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/53.jpg)
LOAD TESTING - 1 DYNO
![Page 54: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/54.jpg)
![Page 55: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/55.jpg)
Bike (Grails) Hike (Play)
LOAD TESTING - 5 DYNOS
![Page 56: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/56.jpg)
LOAD TESTING - 5 DYNOS
![Page 57: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/57.jpg)
![Page 58: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/58.jpg)
LOAD TESTING - 5 DYNOS
GrailsPlay Framework
0 700 1,400 2,100 2,800
Requests / Second
![Page 59: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/59.jpg)
LOAD TESTING - 5 DYNOS + 100 USERS
![Page 60: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/60.jpg)
WHICH LOADS FASTER?
![Page 61: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/61.jpg)
WHICH ACTUALLY LOADS FASTER?
![Page 62: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/62.jpg)
PEN TESTING WITH OWASP ZAP
![Page 63: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/63.jpg)
![Page 64: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/64.jpg)
GRAILS 2 VS. PLAY 2JobsLinkedIn SkillsGoogle TrendsIndeedMailing List TrafficBooks on Amazon2012 ReleasesStack OverflowHacker News
![Page 65: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/65.jpg)
JOBSMarch 19, 2013
GrailsPlay FrameworkSpring MVC
0 400 800 1,200 1,600
Dice
Monster
Indeed
![Page 66: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/66.jpg)
LINKEDIN SKILLSMarch 19, 2013
# People
0 4,000 8,000 12,000 16,000
Grails
Play Framework
Spring MVC
![Page 67: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/67.jpg)
GOOGLE TRENDS Grails Play
![Page 68: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/68.jpg)
INDEED JOB TRENDS
![Page 69: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/69.jpg)
USER MAILING LIST TRAFFICJune 2012
GrailsPlay Framework
800 1,600 2,400 3,200 4,000
March
April
May
![Page 70: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/70.jpg)
USER MAILING LIST TRAFFICMarch 2013
GrailsPlay Framework
800 1,100 1,400 1,700 2,000
December
January
February
![Page 71: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/71.jpg)
BOOKS ON AMAZONMarch 2013
GrailsPlay Framework
3
8
![Page 72: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/72.jpg)
2013 RELEASES
GrailsPlay Framework
1 1
![Page 73: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/73.jpg)
2012 RELEASES
GrailsPlay Framework
6
9
![Page 74: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/74.jpg)
STACKOVERFLOW QUESTIONS
grailsplayframework
3950
10710
![Page 75: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/75.jpg)
HACKER NEWS
Grails 2.0 releasedPlay Framework 2.0 Final released6
195
![Page 76: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/76.jpg)
CONCLUSIONS: CODEFrom a code perspective, very similarframeworks.Code authoring good in both.Grails Plugin Ecosystem is excellent.TDD-Style Development easy with both.Type-safety in Play 2 was really useful, especiallyroutes and upgrades.
![Page 77: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/77.jpg)
CONCLUSIONS: STATISTICAL ANALYSISGrails has better support for FEO (YSlow,PageSpeed)Grails has less LOC! (4 more files, but 20% lesscode)Apache Bench with 10K requests (2 Dynos):
Requests per second: {Play: 2227,Grails: 1053}
Caching significantly helps!
![Page 78: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/78.jpg)
CONCLUSIONS: ECOSYSTEM ANALYSIS"Play" is difficult to search for.Grails is more mature.Play has momentum issues.LinkedIn: more people know Grails than SpringMVC.Play has 3x user mailing list traffic.We had similar experiences with documentationand questions.Outdated documentation is a problem for both.Play has way more hype!
![Page 79: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/79.jpg)
QUESTIONS?Source:
Branches: grails2, play2_javaPresentation*: master/preso
Contact Us:
* Presentation created with , and .
http://ubertracks.com
https://github.com/jamesward/happytrails
jamesward.comraibledesigns.com
Reveal.js Google Charts GitHub Files
![Page 80: PLAY VS. GRAILS SMACKDOWN - Raible Designsstatic.raibledesigns.com/.../presentations/Play_vs_Grails_Smackdown... · PLAY VS. GRAILS SMACKDOWN JAMES WARD AND MATT RAIBLE and Changelog](https://reader033.vdocuments.us/reader033/viewer/2022051600/5a83e7b37f8b9a9d308f1e73/html5/thumbnails/80.jpg)
ACTION!Learn something new*!
* Or prove that we're wrong...