SlideShare a Scribd company logo
ng with
2.0




      Siarzh Miadzvedzeu @siarzh
is

“a web framework for a new era”

                   &

“designed by web developers
 for web developers”
                   Guillaume Bort, creator
0.x   how it was
1.0   how it was
1.2   how it was
2.0   how it was
2.0            is

•   full stack web framework for JVM
•   high-productive
•   asynch & reactive
•   stateless
•   HTTP-centric
•   typesafe
•   scalable
•   open source
•   part of Typesafe Stack 2.0
2.0             is fun and high-productive


•   fast turnaround: change you code and hit reload! :)
•   browser error reporting
•   db evolutions
•   modular and extensible via plugins
•   minimal bootstrap
•   integrated test framework
•   easy cloud deployment (e.g. Heroku)
2.0            is asynch & reactive


•   WebSockets
•   Comet
•   HTTP 1.1 chuncked responses
•   composable streams handling
•   based on event-driven, non-blocking Iteratee I/O
2.0            is HTTP-centric


•   based on HTTP and stateless
•   doesn't fight HTTP or browser
•   clean & easy URL design (via routes)
•   designed to work with HTML5

          “When a web framework starts an architecture fight
          with the web, the framework loses.”
2.0                 is typesafe where it matters




                                      }
•   templates
•   routes
•   configs
•   javascript (via Goggle Closure)
                                          all are compiled
•   LESS stylesheets
•   CoffeeScript

                + browser error reporting
2.0                 getting started

1. download Play 2.0 binary package


2. add play to your PATH:
                              export PATH=$PATH:/path/to/play20


3. create new project:
                              $ play new myFirstApp



4. run the project:
                              $ cd myFirstApp
                              $ play run
2.0                  config
                                             single conf/application.conf:
# This is the main configuration file for the application.

# The application languages
# ~~~~~
application.langs="en"

# Database configuration
# ~~~~~
# You can declare as many datasources as you want.
# By convention, the default datasource is named `default`
#
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
# db.default.user=sa
# db.default.password=

# Evolutions
# ~~~~~
# You can disable evolutions if needed
# evolutionplugin=disabled
...
2.0        IDE support
Eclipse:             $ eclipsify


IntelliJ IDEA:       $ idea



Netbeans:        add to plugins.sbt:
                 resolvers += {
                     "remeniuk repo" at "http://remeniuk.github.com/maven"
                 }


                 libraryDependencies += {
                   "org.netbeans" %% "sbt-netbeans-plugin" % "0.1.4"
                 }



                     $ play netbeans
2.0                  and SBT
              Build.scala                                     plugins.sbt
import sbt._                                  addSbtPlugin("play" % "sbt-plugin" % "2.0")
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

val appName            = "Your application"
val appVersion         = "1.0"

val appDependencies = Seq(
 // Add your project dependencies here,
)

val main = PlayProject(
 appName, appVersion, appDependencies,
  mainLang = SCALA
).settings(
 // Add your own project settings here
)

}
2.0               routes
# Home page
GET /                     controllers.Application.homePage()
GET /home                 controllers.Application.show(page = "home")

# Display a client.
GET /clients/all          controllers.Clients.list()
GET /clients/:id          controllers.Clients.show(id: Long)

# Pagination links, like /clients?page=3
GET /clients              controllers.Clients.list(page: Int ?= 1)

# With regex
GET /orders/$id<[0-9]+>   controllers.Orders.show(id: Long)

# 'name' is all the rest part of the url including '/' symbols
GET /files/*name          controllers.Application.download(name)
2.0                reversed routing

# Hello action
GET /helloBob    controllers.Application.helloBob
GET /hello/:name controllers.Application.hello(name)




// Redirect to /hello/Bob
def helloBob = Action {
  Redirect( routes.Application.hello("Bob") )
}
2.0                   actions
action: (play.api.mvc.Request => play.api.mvc.Result)


Action(parse.text) { request =>
  Ok("<h1>Got: " + request.body + "</h1>").as(HTML).withSession(
                session + ("saidHello" -> "yes")
              ).withHeaders(
                CACHE_CONTROL -> "max-age=3600",
                ETAG -> "xx"
              ).withCookies(
                Cookie("theme", "blue")
              )
}

val   notFound = NotFound
val   pageNotFound = NotFound(<h1>Page not found</h1>)
val   badRequest = BadRequest(views.html.form(formWithErrors))
val   oops = InternalServerError("Oops")
val   anyStatus = Status(488)("Strange response type")
2.0                controllers

package controllers

import play.api.mvc._

object Application extends Controller {

    def index = Action {
      Ok("It works!")
    }

    def hello(name: String) = Action {
      Ok("Hello " + name)
    }

    def goodbye(name: String) = TODO

}
2.0              templates
                                                      …are just functions ;)
views/main.scala.html:     @(title: String)(content: Html)
                           <!DOCTYPE html>
                           <html>
                           <head>
                            <title>@title</title>
                           </head>
                           <body>
                            <section class="content">@content</section>
                           </body>
                           </html>

views/hello.scala.html:    @(name: String = “Guest”)

                           @main(title = "Home") {
                           <h1>Welcome @name! </h1>
                           }




then from Scala class:     val html = views.html.Application.hello(name)
2.0                database evolutions
conf/evolutions/${x}.sql:


        # Add Post

        # --- !Ups
        CREATE TABLE Post (
          id bigint(20) NOT NULL AUTO_INCREMENT,
          title varchar(255) NOT NULL,
          content text NOT NULL,
          postedAt date NOT NULL,
          author_id bigint(20) NOT NULL,
          FOREIGN KEY (author_id) REFERENCES User(id),
          PRIMARY KEY (id)
        );

        # --- !Downs
        DROP TABLE Post;
2.0   browser error reporting
2.0                   access SQL data via Anorm
import anorm._

DB.withConnection { implicit c =>

    val selectCountries = SQL("Select * from Country")

    // Transform the resulting Stream[Row] to a List[(String,String)]
    val countries = selectCountries().map(row =>
      row[String]("code") -> row[String]("name")
    ).toList
}


// using Parser API
import anorm.SqlParser._

val count: Long = SQL("select count(*) from Country").as(scalar[Long].single)

val result:List[String~Int] = {
  SQL("select * from Country")
  .as(get[String]("name")~get[Int]("population") map { case n~p => (n,p) } *)
}
2.0                     and Akka
 def index = Action {          // using actors, coverting Akka Future to Play Promise
  Async {
         (myActor ? "hello").mapTo[String].asPromise.map { response =>
           Ok(response)
         }
     }
 }


 def index = Action {          // execute some task asynchronously
  Async {
         Akka.future { longComputation() }.map { result =>
           Ok("Got " + result)
         }
     }
 }

// schedule sending message 'tick' to testActor every 30 minutes
Akka.system.scheduler.schedule(0 seconds, 30 minutes, testActor, "tick")

// schedule a single task
Akka.system.scheduler.scheduleOnce(10 seconds) {
  file.delete()
}
2.0                 streaming response
def index = Action {

    val file = new java.io.File("/tmp/fileToServe.pdf")
    val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file)

  SimpleResult(
    header = ResponseHeader(200, Map(CONTENT_LENGTH ->
file.length.toString)),
        body = fileContent
    )
}


// Play has a helper for the above:


def index = Action {
  Ok.sendFile(new java.io.File("/tmp/fileToServe.pdf"))
}
2.0                  chunked results
def index = Action {

    val data = getDataStream
    val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(data)

    ChunkedResult(
     header = ResponseHeader(200),
        chunks = dataContent
    )
}


// Play has a helper for the ChunkedResult above:

Ok.stream(dataContent)
2.0                  Comet
lazy val clock: Enumerator[String] = {
  val dateFormat = new SimpleDateFormat("HH mm ss")

     Enumerator.fromCallback { () =>
       Promise.timeout(Some(dateFormat.format(new Date)), 100 milliseconds)
     }
 }

 def liveClock = Action {
   Ok.stream(clock &> Comet(callback = "parent.clockChanged"))
 }

and then in template:
<script type="text/javascript" charset="utf-8">
 var clockChanged = function(time) { // do something }
</script>

<iframe id="comet" src="@routes.Application.liveClock.unique"></iframe>
2.0             WebSockets

just another action in controller:

   def index = WebSocket.using[String] { request =>

       // Log events to the console
       val in = Iteratee.foreach[String](println).mapDone { _ =>
         println("Disconnected")
       }

       // Send a single 'Hello!' message
       val out = Enumerator("Hello!")

       (in, out)
   }
2.0   caching API
      by default uses EHCache, configurable via plugins


      Cache.set("item.key", connectedUser)


      val user: User = Cache.getOrElse[User]("item.key") {
        User.findById(connectedUser)
      }



      // cache HTTP response
      def index = Cached("homePage",600) {
        Action {
          Ok("Hello world")
        }
      }
2.0                 i18n

conf/application.conf:   application.langs="en,en-US,fr"


conf/messages.en:
                         home.title=File viewer
                         files.summary=The disk {1} contains {0} file(s).



 from Scala class:       val title   = Messages("home.title")
                         val titleFR = Messages("home.title")(Lang(“fr"))
                         val summary = Messages("files.summary", d.files.length, d.name)

  from template:         <h1>@Messages("home.title")</h1>
2.0                        testing
                                                   …using specs2 by default
"Computer model" should {


    "be retrieved by id" in {
     running(FakeApplication(additionalConfiguration = inMemoryDatabase())) {

            val Some(macintosh) = Computer.findById(21)


            macintosh.name must equalTo("Macintosh")
            macintosh.introduced must beSome.which(dateIs(_, "1984-01-24"))

        }
    }
}
2.0             testing templates


 "render index template" in {
   val html = views.html.index("Coco")

     contentType(html) must equalTo("text/html")
     contentAsString(html) must contain("Hello Coco")
 }
2.0               testing controllers


"respond to the index Action" in {
  val result = controllers.Application.index("Bob")(FakeRequest())

    status(result) must equalTo(OK)
    contentType(result) must beSome("text/html")
    charset(result) must beSome("utf-8")
    contentAsString(result) must contain("Hello Bob")
}
2.0              testing routes

"respond to the index Action" in {
  val Some(result) = routeAndCall(FakeRequest(GET, "/Bob"))

    status(result) must equalTo(OK)
    contentType(result) must beSome("text/html")
    charset(result) must beSome("utf-8")
    contentAsString(result) must contain("Hello Bob")
}
2.0               testing server


"run in a server" in {
  running(TestServer(3333)) {

        await( WS.url("http://localhost:3333").get ).status must equalTo(OK)

    }
}
2.0                 testing with browser
                                 …using Selenium WebDriver with FluentLenium

"run in a browser" in {
    running(TestServer(3333), HTMLUNIT) { browser =>

        browser.goTo("http://localhost:3333")
        browser.$("#title").getTexts().get(0) must equalTo("Hello Guest")

        browser.$("a").click()

        browser.url must equalTo("http://localhost:3333/Coco")
        browser.$("#title").getTexts().get(0) must equalTo("Hello Coco")


    }
}
2.0   Demo
2.0         Resources

http://www.playframework.org/
https://github.com/playframework/Play20/wiki
http://www.parleys.com/#st=5&id=3143&sl=4
http://www.parleys.com/#st=5&id=3144&sl=13
http://www.parleys.com/#id=3081&st=5
http://vimeo.com/41094673
2.0   Questions


         ?

More Related Content

What's hot (20)

PPT
Presentation
Manav Prasad
 
PPTX
Python Code Camp for Professionals 1/4
DEVCON
 
PDF
WebGUI Developers Workshop
Plain Black Corporation
 
PPT
Intro to-ant
Manav Prasad
 
PDF
Play vs Rails
Daniel Cukier
 
PPT
Ant
Manav Prasad
 
PDF
OSCON Google App Engine Codelab - July 2010
ikailan
 
PDF
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Ryan Weaver
 
PDF
HTML5 JavaScript APIs
Remy Sharp
 
PDF
A Little Backbone For Your App
Luca Mearelli
 
PDF
Node.js in action
Simon Su
 
PPTX
Python Code Camp for Professionals 2/4
DEVCON
 
PDF
Burn down the silos! Helping dev and ops gel on high availability websites
Lindsay Holmwood
 
PDF
Keeping the frontend under control with Symfony and Webpack
Ignacio Martín
 
KEY
CodeIgniter 3.0
Phil Sturgeon
 
PPTX
REST with Eve and Python
PiXeL16
 
PDF
Scala active record
鉄平 土佐
 
PDF
Phoenix + Reactで 社内システムを 密かに作ってる
Takahiro Kobaru
 
PDF
Filling the flask
Jason Myers
 
PDF
A re introduction to webpack - reactfoo - mumbai
Praveen Puglia
 
Presentation
Manav Prasad
 
Python Code Camp for Professionals 1/4
DEVCON
 
WebGUI Developers Workshop
Plain Black Corporation
 
Intro to-ant
Manav Prasad
 
Play vs Rails
Daniel Cukier
 
OSCON Google App Engine Codelab - July 2010
ikailan
 
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Ryan Weaver
 
HTML5 JavaScript APIs
Remy Sharp
 
A Little Backbone For Your App
Luca Mearelli
 
Node.js in action
Simon Su
 
Python Code Camp for Professionals 2/4
DEVCON
 
Burn down the silos! Helping dev and ops gel on high availability websites
Lindsay Holmwood
 
Keeping the frontend under control with Symfony and Webpack
Ignacio Martín
 
CodeIgniter 3.0
Phil Sturgeon
 
REST with Eve and Python
PiXeL16
 
Scala active record
鉄平 土佐
 
Phoenix + Reactで 社内システムを 密かに作ってる
Takahiro Kobaru
 
Filling the flask
Jason Myers
 
A re introduction to webpack - reactfoo - mumbai
Praveen Puglia
 

Similar to Play!ng with scala (20)

PDF
Scalatra 2.2
Ivan Porto Carrero
 
PDF
Play framework
Andrew Skiba
 
PDF
Play Framework and Activator
Kevin Webber
 
PDF
Scaling business app development with Play and Scala
Peter Hilton
 
PPT
Intoduction on Playframework
Knoldus Inc.
 
PDF
Play framework: lessons learned
Peter Hilton
 
PDF
Scala services in action
Underscore
 
PDF
Lift talk
Winston Chen
 
PPTX
ql.io: Consuming HTTP at Scale
Subbu Allamaraju
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PPTX
ql.io at NodePDX
Subbu Allamaraju
 
KEY
Modular Web Applications With Netzke
netzke
 
KEY
Gaelyk - JFokus 2011 - Guillaume Laforge
Guillaume Laforge
 
KEY
Unfiltered Unveiled
Wilfred Springer
 
PDF
Using Play Framework 2 in production
Christian Papauschek
 
PDF
Scala4sling
day
 
PDF
Grails 101
David Jacobs
 
Scalatra 2.2
Ivan Porto Carrero
 
Play framework
Andrew Skiba
 
Play Framework and Activator
Kevin Webber
 
Scaling business app development with Play and Scala
Peter Hilton
 
Intoduction on Playframework
Knoldus Inc.
 
Play framework: lessons learned
Peter Hilton
 
Scala services in action
Underscore
 
Lift talk
Winston Chen
 
ql.io: Consuming HTTP at Scale
Subbu Allamaraju
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
ql.io at NodePDX
Subbu Allamaraju
 
Modular Web Applications With Netzke
netzke
 
Gaelyk - JFokus 2011 - Guillaume Laforge
Guillaume Laforge
 
Unfiltered Unveiled
Wilfred Springer
 
Using Play Framework 2 in production
Christian Papauschek
 
Scala4sling
day
 
Grails 101
David Jacobs
 
Ad

Recently uploaded (20)

PDF
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
PDF
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
PDF
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
PPTX
AVL ( audio, visuals or led ), technology.
Rajeshwri Panchal
 
PDF
RAT Builders - How to Catch Them All [DeepSec 2024]
malmoeb
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
PDF
Build with AI and GDG Cloud Bydgoszcz- ADK .pdf
jaroslawgajewski1
 
PDF
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
PPTX
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
PDF
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
PPTX
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
PDF
Per Axbom: The spectacular lies of maps
Nexer Digital
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PDF
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
PPTX
Farrell_Programming Logic and Design slides_10e_ch02_PowerPoint.pptx
bashnahara11
 
PDF
NewMind AI Weekly Chronicles – July’25, Week III
NewMind AI
 
PPTX
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PDF
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
AVL ( audio, visuals or led ), technology.
Rajeshwri Panchal
 
RAT Builders - How to Catch Them All [DeepSec 2024]
malmoeb
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
Build with AI and GDG Cloud Bydgoszcz- ADK .pdf
jaroslawgajewski1
 
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
Per Axbom: The spectacular lies of maps
Nexer Digital
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
Farrell_Programming Logic and Design slides_10e_ch02_PowerPoint.pptx
bashnahara11
 
NewMind AI Weekly Chronicles – July’25, Week III
NewMind AI
 
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
Ad

Play!ng with scala

  • 1. ng with 2.0 Siarzh Miadzvedzeu @siarzh
  • 2. is “a web framework for a new era” & “designed by web developers for web developers” Guillaume Bort, creator
  • 3. 0.x how it was
  • 4. 1.0 how it was
  • 5. 1.2 how it was
  • 6. 2.0 how it was
  • 7. 2.0 is • full stack web framework for JVM • high-productive • asynch & reactive • stateless • HTTP-centric • typesafe • scalable • open source • part of Typesafe Stack 2.0
  • 8. 2.0 is fun and high-productive • fast turnaround: change you code and hit reload! :) • browser error reporting • db evolutions • modular and extensible via plugins • minimal bootstrap • integrated test framework • easy cloud deployment (e.g. Heroku)
  • 9. 2.0 is asynch & reactive • WebSockets • Comet • HTTP 1.1 chuncked responses • composable streams handling • based on event-driven, non-blocking Iteratee I/O
  • 10. 2.0 is HTTP-centric • based on HTTP and stateless • doesn't fight HTTP or browser • clean & easy URL design (via routes) • designed to work with HTML5 “When a web framework starts an architecture fight with the web, the framework loses.”
  • 11. 2.0 is typesafe where it matters } • templates • routes • configs • javascript (via Goggle Closure) all are compiled • LESS stylesheets • CoffeeScript + browser error reporting
  • 12. 2.0 getting started 1. download Play 2.0 binary package 2. add play to your PATH: export PATH=$PATH:/path/to/play20 3. create new project: $ play new myFirstApp 4. run the project: $ cd myFirstApp $ play run
  • 13. 2.0 config single conf/application.conf: # This is the main configuration file for the application. # The application languages # ~~~~~ application.langs="en" # Database configuration # ~~~~~ # You can declare as many datasources as you want. # By convention, the default datasource is named `default` # db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play" # db.default.user=sa # db.default.password= # Evolutions # ~~~~~ # You can disable evolutions if needed # evolutionplugin=disabled ...
  • 14. 2.0 IDE support Eclipse: $ eclipsify IntelliJ IDEA: $ idea Netbeans: add to plugins.sbt: resolvers += { "remeniuk repo" at "http://remeniuk.github.com/maven" } libraryDependencies += { "org.netbeans" %% "sbt-netbeans-plugin" % "0.1.4" } $ play netbeans
  • 15. 2.0 and SBT Build.scala plugins.sbt import sbt._ addSbtPlugin("play" % "sbt-plugin" % "2.0") import Keys._ import PlayProject._ object ApplicationBuild extends Build { val appName = "Your application" val appVersion = "1.0" val appDependencies = Seq( // Add your project dependencies here, ) val main = PlayProject( appName, appVersion, appDependencies, mainLang = SCALA ).settings( // Add your own project settings here ) }
  • 16. 2.0 routes # Home page GET / controllers.Application.homePage() GET /home controllers.Application.show(page = "home") # Display a client. GET /clients/all controllers.Clients.list() GET /clients/:id controllers.Clients.show(id: Long) # Pagination links, like /clients?page=3 GET /clients controllers.Clients.list(page: Int ?= 1) # With regex GET /orders/$id<[0-9]+> controllers.Orders.show(id: Long) # 'name' is all the rest part of the url including '/' symbols GET /files/*name controllers.Application.download(name)
  • 17. 2.0 reversed routing # Hello action GET /helloBob controllers.Application.helloBob GET /hello/:name controllers.Application.hello(name) // Redirect to /hello/Bob def helloBob = Action { Redirect( routes.Application.hello("Bob") ) }
  • 18. 2.0 actions action: (play.api.mvc.Request => play.api.mvc.Result) Action(parse.text) { request => Ok("<h1>Got: " + request.body + "</h1>").as(HTML).withSession( session + ("saidHello" -> "yes") ).withHeaders( CACHE_CONTROL -> "max-age=3600", ETAG -> "xx" ).withCookies( Cookie("theme", "blue") ) } val notFound = NotFound val pageNotFound = NotFound(<h1>Page not found</h1>) val badRequest = BadRequest(views.html.form(formWithErrors)) val oops = InternalServerError("Oops") val anyStatus = Status(488)("Strange response type")
  • 19. 2.0 controllers package controllers import play.api.mvc._ object Application extends Controller { def index = Action { Ok("It works!") } def hello(name: String) = Action { Ok("Hello " + name) } def goodbye(name: String) = TODO }
  • 20. 2.0 templates …are just functions ;) views/main.scala.html: @(title: String)(content: Html) <!DOCTYPE html> <html> <head> <title>@title</title> </head> <body> <section class="content">@content</section> </body> </html> views/hello.scala.html: @(name: String = “Guest”) @main(title = "Home") { <h1>Welcome @name! </h1> } then from Scala class: val html = views.html.Application.hello(name)
  • 21. 2.0 database evolutions conf/evolutions/${x}.sql: # Add Post # --- !Ups CREATE TABLE Post ( id bigint(20) NOT NULL AUTO_INCREMENT, title varchar(255) NOT NULL, content text NOT NULL, postedAt date NOT NULL, author_id bigint(20) NOT NULL, FOREIGN KEY (author_id) REFERENCES User(id), PRIMARY KEY (id) ); # --- !Downs DROP TABLE Post;
  • 22. 2.0 browser error reporting
  • 23. 2.0 access SQL data via Anorm import anorm._ DB.withConnection { implicit c => val selectCountries = SQL("Select * from Country") // Transform the resulting Stream[Row] to a List[(String,String)] val countries = selectCountries().map(row => row[String]("code") -> row[String]("name") ).toList } // using Parser API import anorm.SqlParser._ val count: Long = SQL("select count(*) from Country").as(scalar[Long].single) val result:List[String~Int] = { SQL("select * from Country") .as(get[String]("name")~get[Int]("population") map { case n~p => (n,p) } *) }
  • 24. 2.0 and Akka def index = Action { // using actors, coverting Akka Future to Play Promise Async { (myActor ? "hello").mapTo[String].asPromise.map { response => Ok(response) } } } def index = Action { // execute some task asynchronously Async { Akka.future { longComputation() }.map { result => Ok("Got " + result) } } } // schedule sending message 'tick' to testActor every 30 minutes Akka.system.scheduler.schedule(0 seconds, 30 minutes, testActor, "tick") // schedule a single task Akka.system.scheduler.scheduleOnce(10 seconds) { file.delete() }
  • 25. 2.0 streaming response def index = Action { val file = new java.io.File("/tmp/fileToServe.pdf") val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file) SimpleResult( header = ResponseHeader(200, Map(CONTENT_LENGTH -> file.length.toString)), body = fileContent ) } // Play has a helper for the above: def index = Action { Ok.sendFile(new java.io.File("/tmp/fileToServe.pdf")) }
  • 26. 2.0 chunked results def index = Action { val data = getDataStream val dataContent: Enumerator[Array[Byte]] = Enumerator.fromStream(data) ChunkedResult( header = ResponseHeader(200), chunks = dataContent ) } // Play has a helper for the ChunkedResult above: Ok.stream(dataContent)
  • 27. 2.0 Comet lazy val clock: Enumerator[String] = { val dateFormat = new SimpleDateFormat("HH mm ss") Enumerator.fromCallback { () => Promise.timeout(Some(dateFormat.format(new Date)), 100 milliseconds) } } def liveClock = Action { Ok.stream(clock &> Comet(callback = "parent.clockChanged")) } and then in template: <script type="text/javascript" charset="utf-8"> var clockChanged = function(time) { // do something } </script> <iframe id="comet" src="@routes.Application.liveClock.unique"></iframe>
  • 28. 2.0 WebSockets just another action in controller: def index = WebSocket.using[String] { request => // Log events to the console val in = Iteratee.foreach[String](println).mapDone { _ => println("Disconnected") } // Send a single 'Hello!' message val out = Enumerator("Hello!") (in, out) }
  • 29. 2.0 caching API by default uses EHCache, configurable via plugins Cache.set("item.key", connectedUser) val user: User = Cache.getOrElse[User]("item.key") { User.findById(connectedUser) } // cache HTTP response def index = Cached("homePage",600) { Action { Ok("Hello world") } }
  • 30. 2.0 i18n conf/application.conf: application.langs="en,en-US,fr" conf/messages.en: home.title=File viewer files.summary=The disk {1} contains {0} file(s). from Scala class: val title = Messages("home.title") val titleFR = Messages("home.title")(Lang(“fr")) val summary = Messages("files.summary", d.files.length, d.name) from template: <h1>@Messages("home.title")</h1>
  • 31. 2.0 testing …using specs2 by default "Computer model" should { "be retrieved by id" in { running(FakeApplication(additionalConfiguration = inMemoryDatabase())) { val Some(macintosh) = Computer.findById(21) macintosh.name must equalTo("Macintosh") macintosh.introduced must beSome.which(dateIs(_, "1984-01-24")) } } }
  • 32. 2.0 testing templates "render index template" in { val html = views.html.index("Coco") contentType(html) must equalTo("text/html") contentAsString(html) must contain("Hello Coco") }
  • 33. 2.0 testing controllers "respond to the index Action" in { val result = controllers.Application.index("Bob")(FakeRequest()) status(result) must equalTo(OK) contentType(result) must beSome("text/html") charset(result) must beSome("utf-8") contentAsString(result) must contain("Hello Bob") }
  • 34. 2.0 testing routes "respond to the index Action" in { val Some(result) = routeAndCall(FakeRequest(GET, "/Bob")) status(result) must equalTo(OK) contentType(result) must beSome("text/html") charset(result) must beSome("utf-8") contentAsString(result) must contain("Hello Bob") }
  • 35. 2.0 testing server "run in a server" in { running(TestServer(3333)) { await( WS.url("http://localhost:3333").get ).status must equalTo(OK) } }
  • 36. 2.0 testing with browser …using Selenium WebDriver with FluentLenium "run in a browser" in { running(TestServer(3333), HTMLUNIT) { browser => browser.goTo("http://localhost:3333") browser.$("#title").getTexts().get(0) must equalTo("Hello Guest") browser.$("a").click() browser.url must equalTo("http://localhost:3333/Coco") browser.$("#title").getTexts().get(0) must equalTo("Hello Coco") } }
  • 37. 2.0 Demo
  • 38. 2.0 Resources http://www.playframework.org/ https://github.com/playframework/Play20/wiki http://www.parleys.com/#st=5&id=3143&sl=4 http://www.parleys.com/#st=5&id=3144&sl=13 http://www.parleys.com/#id=3081&st=5 http://vimeo.com/41094673
  • 39. 2.0 Questions ?