• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Play!ng with scala
 

Play!ng with scala

on

  • 6,752 views

Play framework 2.0 presentation for Scala developers.

Play framework 2.0 presentation for Scala developers.

Statistics

Views

Total Views
6,752
Views on SlideShare
6,106
Embed Views
646

Actions

Likes
17
Downloads
144
Comments
0

4 Embeds 646

http://scala.by 635
http://localhost 6
https://si0.twimg.com 3
https://twitter.com 2

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Play!ng with scala Play!ng with scala Presentation Transcript

    • ng with2.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• doesnt 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 started1. download Play 2.0 binary package2. add play to your PATH: export PATH=$PATH:/path/to/play203. create new project: $ play new myFirstApp4. 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.Driverdb.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 supportEclipse: $ eclipsifyIntelliJ IDEA: $ ideaNetbeans: 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.sbtimport 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 pageGET / 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=3GET /clients controllers.Clients.list(page: Int ?= 1)# With regexGET /orders/$id<[0-9]+> controllers.Orders.show(id: Long)# name is all the rest part of the url including / symbolsGET /files/*name controllers.Application.download(name)
    • 2.0 reversed routing# Hello actionGET /helloBob controllers.Application.helloBobGET /hello/:name controllers.Application.hello(name)// Redirect to /hello/Bobdef helloBob = Action { Redirect( routes.Application.hello("Bob") )}
    • 2.0 actionsaction: (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 = NotFoundval 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 controllerspackage controllersimport 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 evolutionsconf/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 Anormimport 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 APIimport 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 minutesAkka.system.scheduler.schedule(0 seconds, 30 minutes, testActor, "tick")// schedule a single taskAkka.system.scheduler.scheduleOnce(10 seconds) { file.delete()}
    • 2.0 streaming responsedef 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 resultsdef 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 Cometlazy 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 WebSocketsjust 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 i18nconf/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 Resourceshttp://www.playframework.org/https://github.com/playframework/Play20/wikihttp://www.parleys.com/#st=5&id=3143&sl=4http://www.parleys.com/#st=5&id=3144&sl=13http://www.parleys.com/#id=3081&st=5http://vimeo.com/41094673
    • 2.0 Questions ?