What is Play?
Play is a full stack framework that gives you everything to build a typical web application.
Key Features:
A variety of features and qualities makes Play productive and fun to use:
Benefits
Key Features:
A variety of features and qualities makes Play productive and fun to use:
- Declarative application URL scheme configuration
- Type-safe mapping from HTTP to an idiomatic Scala API
- Type-safe template syntax
- Architecture that embraces HTML5 client technologies
- Live code changes when you reload the page in your web browser
- Full-stack web framework features, including persistence, security, and
internationalization
- A view template—A template that generates HTML
- A controller action—A Scala function that renders the view
- Route configuration—Configuration to map the URL to the action
- The model—Scala code that defines the product structure, and some test data
Benefits
- Asynchronous WS Client
- Non-blocking architecture ( while waiting for remote host, server is free to process other requests)
- Dependency management
- Share nothing architecture
- Code Auto-Reload
- In-buit JSON Library
Directory Structure
This directory structure is common to all Play applications. The top-level directories group the files as follows:
Default files in a new play application:
- app—Application source code
- conf—Configuration files and data
- project—Project build scripts
- public—Publicly accessible static files
- test—Automated tests
Default files in a new play application:
- .gitignore
- app/controllers/Application.scala
- app/views/index.scala.html
- app/views/main.scala.html
- conf/application.conf
- conf/routes
- project/build.properties
- project/Build.scala
- project/plugins.sbt
- public/images/favicon.png
- public/javascripts/jquery-1.7.1.min.js
- public/stylesheets/main.css
- test/ApplicationSpec.scala
- test/IntegrationSpec.scala
Commands to create a typical Play Application
play - play launches the play console that provides variety of commands to work on the application like compile, console. Using console in play console launches Scala REPL. Within Scala console you can do things like render a template which is a scala function example: view.html.index.render("name")
play new myFirstApp -- Creates a new play application named 'myFirstApp'
cd myFristApp
play run -- Starts Play Server and launches the Play application
To view on web browser: Open page: localhost:9000
play new myFirstApp -- Creates a new play application named 'myFirstApp'
cd myFristApp
play run -- Starts Play Server and launches the Play application
To view on web browser: Open page: localhost:9000
Basics
- Action method generates the HTTP Response example Ok()
- conf/routes HTTP routing configuration file that maps GET / HTTP requests to a method invocation. Example: GET / controllers.Application.index()
- Dependency management: allow the user to specify direct dependencies for his project and automatically include necessary transitive dependencies.
- Use 'evicted' command in play console to find the incompatible libraries.
- Versioning semantics are different for artifacts in scala ecosystem like: A.B.C where the minor indicates the compatibility unlike major in Java ecosystem.
- while defining the library dependency, '%%' indicates to look for version that is compatible with scala version that is in use.
- If activator or play is not able to find the dependency then it may not be available on your scala version.
- If SBT detects any incompatibility you may need to change the scala version.
- To exclude transitive dependencies use "org.abc" %% "def" % "2.3.0" excludeAll ( exclusionRule (organization = "javax.jms"))
- To execute a code async in the controller we can use scala.concurrent.future to execute the block of code separately.
- Play provides macro that generates JSON serializers following a specific pattern as defined in a case class. For example to define a JSON serializer for case class Employee( name: String, ssn: String)
implicit val employeeWrites = Json.writes [Employee]
to read:
implicit val employeeReads = Json.reads [Employee]
to define Json reads and writes without play macro:
implicit val employeeReads: Reads[Employee] = (
((__ \ "name").read[String]) and
((__ \ "ssn").read[String]) and
((__ \ "phone").read(Reads.optionWithNull[String])) and
((__ \ "status").readNullable[String])
)(Employee.apply _)
implicit val employeeWrites = writes[Employee] {
case Employee(name, ssn) =>
Json.obj("name" -> name, "ssn" -> ssn)
}
- To interpret the action request as JSON data we can use Action (parse.json) on implicit request than simple Action.
- To validate the Json data in action request against any case class, request.body.validate[Employee] match { JsSuccess(Employee, _) => .. JsError(errors) => .. }
def update = Action(parse.json) = { implicit request =>
request.body.validate[Employee] match {
JsSuccess(Employee, _) =>
TODO
JsError(errors) =>
BadRequest
}
}
- By specifying Employee case class in the body parser, we can avoid explicit validation of parsed object. Implicit validation throws BadRequest 400 when encountered JsError.
def update = Action(parse.json[Employee]) = { implicit request =>
TODO
}
}
Retrieving current application configuration
play.api.configuration will provide type safe access to current configuration parameter values with methods that read parameters of different types, namely String, Int, Boolean. We can also access sub-configuration using getConfig() method. Implicit current is an instance of play.api.Application
import play.api.Play.current
val defaultURL = current.configuration.getString("db.default.url")
val defaultConfiguration = current.configuration.getConfig("db.default")
import play.api.Play.current
val defaultURL = current.configuration.getString("db.default.url")
val defaultConfiguration = current.configuration.getConfig("db.default")
Domain Model
Scala case classes can be used to conveniently define the domain model classes. One advantage would be immutability, persist the model using persistence API. Play includes the Anorm persistence API so that you can build a complete web application, including SQL database access, without any additional libraries. Another library named Slick offers Scala-based APIs for RDMS access.
Scheduled Jobs
Play doesn’t provide scheduling functionality directly, but instead integrates with Akka, a library for actor-based concurrency that’s included with Play. We won’t need a user interface; instead we’ll create and schedule the actor when the Play application starts. In a typical play application, app/Global.scala has the code that creates and schedules an actor for each when our application starts. We will be using Akka’s scheduler API directly here, with implicit conver- sions from the akka.util.duration._ package that converts expressions like 30 min- utes to an akka.util.Duration instance.
Global.scala
import akka.actor.{Actor, Props}
import models.Item
import play.api.libs.concurrent.Akka
import play.api.GlobalSettings
import play.api.templates.Html
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import play.api.Play.current
object Global extends GlobalSettings {
for ( item <- Item.find()) {
val actor = Akka.system.actorOf ( Props ( new ItemActor (item))
Akka.system.scheduler.schedule ( 0.seconds, 30.minutes, actor, "send")
}
}
Actor Class:
import java.util.Date
import models.Item
class ItemActor (item: String) extends Actor {
def Receive = {
case "send" => val itemList = Item.find (item)
val html = views.html.pickList(warehouse, pickList, new Date)
send(html)
}
}
Global.scala
import akka.actor.{Actor, Props}
import models.Item
import play.api.libs.concurrent.Akka
import play.api.GlobalSettings
import play.api.templates.Html
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import play.api.Play.current
object Global extends GlobalSettings {
for ( item <- Item.find()) {
val actor = Akka.system.actorOf ( Props ( new ItemActor (item))
Akka.system.scheduler.schedule ( 0.seconds, 30.minutes, actor, "send")
}
}
Actor Class:
import java.util.Date
import models.Item
class ItemActor (item: String) extends Actor {
def Receive = {
case "send" => val itemList = Item.find (item)
val html = views.html.pickList(warehouse, pickList, new Date)
send(html)
}
}