play framework training by neelkanth sachdeva @ scala traits event , new delhi by knoldus software...

Download Play framework training by Neelkanth Sachdeva @ Scala traits event , New Delhi by Knoldus Software LLP

If you can't read please download the document

Upload: knoldus-software-llp

Post on 16-Apr-2017

4.057 views

Category:

Technology


1 download

TRANSCRIPT

Knoldus Software LLPNew Delhi , Indiawww.knoldus.com

Neelkanth [email protected]

Kick start to

I am

Software Consultant @ Knoldus Software LLP , New Delhi ( The only Typesafe partner in Asia ) http://www.knoldus.com/

Co-Founder @ My Cell Was Stolen http://www.mycellwasstolen.com/

Introduction to

Agenda

IntroductionOverview

Features of Play

Components of Play

Action , Controller ,Result

Routes

The template engine

HTTP Form

Other conceptsWeb Services

Working with XML

Working with JSONDevelopment

Developing a application using PlayDeployment

Deploying the Play application to Heroku

Overview

Play framework is :

Created by Guillaume Bort, while working at Zenexity.

An open source web application framework.

Lightweight, stateless, web-friendly architecture.

Written in Scala and Java.

Support for the Scala programming language has been available since version 1.1 of the framework.

Features of Play!

Stateless architecture

Less configuration: Download, unpack and develop.

Faster Testing

More elegant API

Modular architecture

Asynchronous I/O

Stateless Architecture-The base of Play

Each request as an independent transaction that is unrelated to any previous request so that the communication consists of independent pairs of requests and responses.

Basic Play ! components

JBoss Netty for the web server.

Scala for the template engine.

Built in hot-reloading

sbt for dependency management

Action, Controller & Result

Action :- Most of the requests received by a Play application are handled by an Action.A play.api.mvc.Action is basically a (play.api.mvc.Request => play.api.mvc.Result) function that handles a request and generates a result to be sent to the client.

val hello = Action { implicit request => Ok("Request Data [" + request + "]") }

Controllers :-Controllers are action generators.A singleton object that generates Action values.The simplest use case for defining an action generator is a method with no parameters that returns an Action value :

import play.api.mvc._

object Application extends Controller {

def index = Action { Ok("Controller Example") }

}

val ok = Ok("Hello world!")

val notFound = NotFound

val pageNotFound = NotFound(Page not found)

val badRequest = BadRequest(views.html.form(formWithErrors))

val oops = InternalServerError("Oops")

val anyStatus = Status(488)("Strange response type")

Redirect(/url)

Results

package controllers

import play.api._import play.api.mvc._

object Application extends Controller { def index = Action { //Do something Ok } }

Controller ( Action Generator )

index Action

Result

HTTP routing :

An HTTP request is treated as an event by the MVC framework. This event contains two major pieces of information:

The request path (such as /local/employees ), including the query string.

The HTTP methods (GET, POST, ).

Routes

The conf/routes file

# Home pageGET / controllers.Application.index

GET - Request Type ( GET , POST , ... ). / - URL pattern.Application - The controller object.Index - Method (Action) which is being called.

The template engine

Based on Scala

Compact, expressive, and fluid

Easy to learn

Play Scala template is a simple text file, that contains small blocks of Scala code. They can generate any text-based format, such as HTML, XML or CSV.

Compiled as standard Scala functions.

Because Templates are compiled, so you will see any errors right in your browser

@main

views/main.scala.html template act as a main layout template. e.g This is our Main template.

@(title: String)(content: Html)

@title @content

As you can see, this template takes two parameters: a title and an HTML content block. Now we can use it from any other template e.gviews/Application/index.scala.html template:

@main(title = "Home") { Home page }

Note:We sometimes use named parameters(like@main(title = "Home"), sometimes not like@main("Home"). It is as you want, choose whatever is clearer in a specific context.

Syntax : the magic @ character

Every time this character is encountered, it indicates the begining of a Scala statement.

@employee.name // Scala statement starts here Template parameters

A template is simply a function, so it needs parameters, which must be declared on the first line of the template file.

@(employees: List[Employee], employeeForm: Form[String])

Iterating : You can use the for keyword in order to iterate.

  • @for(c nonEmptyText, "password" -> nonEmptyText)) }

    This form can generate a (String, String) result value from Map[String, String] data:

    The corresponding template

    @(loginForm :Form[(String, String)],message:String)

    @import helper._

    @main(message){@helper.form(action = routes.FormExampleController.authenticateUser) { @message @inputText( loginForm("email"), '_label -> "Email ", '_help -> "Enter a valid email address." )

    @inputPassword( loginForm("password"), '_label -> "Password", '_help -> "A password must be at least 6 characters." ) } }

    Putting the validation

    We can apply the validation at the time of defining the form controls like :

    val loginForm = Form( tuple( "email" -> email, "password" -> nonEmptyText(minLength = 6)))

    email means : The textbox would accept an valid Email.

    nonEmptyText(minLength = 6) means : The textbox would accept atleast 6 characters

    Constructing complex objects

    You can use complex form mapping as well.Here is the simple example :

    case class User(name: String, age: Int)

    val userForm = Form( mapping( "name" -> text, "age" -> number)(User.apply)(User.unapply))

    Calling WebServices

    Sometimes there becomes a need to call other HTTP services from within a Play application.

    This is supported in Play via play.api.libs.ws.WS library, which provides a way to make asynchronousHTTP calls.

    It returns a Promise[play.api.libs.ws.Response].

    Making an HTTP call

    GET request :val returnedValue : Promise[ws.Response] = WS.url("http://mysite.com").get()

    POST request : WS.url("http://mysite.com").post(Map("key" -> Seq("value")))

    Example

    Lets make a WS GET request onScalaJobz.com API.

    Result :This call would return the JSON of jobs from scalajobz.com.

    Route definition:GET /jobs controllers.Application.jobs

    Controller :

    /** * Calling web services */

    def jobs = Action { WS.url("http://www.scalajobz.com/getAllJobs").get().map { response => println(response.json) } Ok("Jobs Fetched") }

    Working with XML

    An XML request is an HTTP request using a valid.

    XML payload as the request body.

    It must specify the text/xml MIME type in its Content-Type header.

    By default an Action uses a any content body parser, which lets you retrieve the body as XML (as a NodeSeq)

    Defining The Route :

    POST /sayHello controllers.Application.sayHello

    Defining The Controller :

    package controllers

    import play.api.mvc.Actionimport play.api.mvc.Controller

    object Application extends Controller {

    def sayHello = Action { request => request.body.asXml.map { xml => (xml \\ "name" headOption).map(_.text).map { name => Ok("Hello " + name + " ") }.getOrElse { BadRequest("Missing parameter [name]") } }.getOrElse { BadRequest("Expecting Xml data") } }}

    Let us make a POST request containing XML data

    Working with JSON

    1. A JSON request is an HTTP request using a valid JSON

    payload as request body.

    2. It must specify the text/json or application/json mime type in its Content-Type header.

    3. By default an Action uses an any content body parser, which lets you retrieve the body as JSON.(as a JsValue).

    Defining The Route :

    POST /greet controllers.Application.greet

    Defining The Controller :

    package controllers

    import play.api.mvc.Controllerimport play.api.mvc.Action

    object Application extends Controller {

    def greet = Action { request => request.body.asJson.map { json => (json \ "name").asOpt[String].map { name => Ok("Hello " + name + " ") }.getOrElse { BadRequest("Missing parameter [name]") } }.getOrElse { BadRequest("Expecting Json data") } }

    }

    Lets make a POST request containing JSON data

    Lets then

    Prerequisites

    1. Download the Scala SDK from here.http://scala-ide.org/download/sdk.html2. A basic knowledge of the Eclipse user interface

    Setting up Play 2.1

    1. Download Play framework 2.1.3 from http://www.playframework.org.

    2. Unzip it in your preferred location. Let's say /path/to/play for the purpose of this document.

    3. Add the Play folder to your system PATH export PATH=$PATH:/path/to/play

    Creating a Play 2.1.3 application

    In your development folder, ask Play to create a new web application, as a simple Scala application.

    Creating a new project

    Clone MyOffice app from here as

    git clone [email protected]:knoldus/ScalaTraits-August2013-Play.git

    Importing the project in to eclipse IDE

    Running the project

    The Project Structure

    The project MyOffice mainly contains :

    app / : Contains the applications core, split between models, controllers and views directories.

    Conf / : Contains the configuration files. Especially the main application.conf file,the routes definition files. routes defines the routes of the UI calls.

    project : Contains the build scripts.

    public / : Contains all the publicly available resources, which includes JavaScript, stylesheets and images directories.

    test / : Contains all the application tests.

    Edit the conf/routes file:

    # Home pageGET / controllers.Application.index # MyOffice GET /employees controllers.MyOfficeController.employeesPOST /newEmployee controllers.MyOfficeController.newEmployeePOST /employee/:id/deleteEmployee controllers.MyOfficeController.deleteEmployee(id: Long)

    Add the MyOfficeController.scala object under controllers & define the methods those will perform the action.

    package controllers

    import play.api.mvc._

    object MyOfficeController extends Controller {

    /** * Total Employees In Office */ def employees = TODO

    /** * Add A New Employee */ def newEmployee = TODO

    /** * Remove An Employee */ def deleteEmployee(id: Long) = TODO

    }

    As you see we use TODO to define our actionimplementations. Because we haven't write the action implementations yet, we can use the built-in TODO action that will return a 501 Not Implemented HTTP response.Now lets hit the http://localhost:9000/employees & see what we find.

    Lets define the models in our MyOffice application that will perform the business logic.

    Preparing the Employee model

    Before continuing the implementation we need to define how theEmployee looks like in our application. Create a case class for it in the app/models/Employee.scala file.

    case class Employee(id: Long, name : String)

    Each Employee is having an : Id - That uniquely identifies the Employee

    name - Name of the Employee

    The Employee model

    package models

    case class Employee(id: Long, name: String)

    object Employee {

    /** * All Employees In Office */ def allEmployees(): List[Employee] = Nil

    /** * Adding A New Employee */ def newEmployee(nameOfEmployee: String) {}

    /** * Delete Employee * @param id : id Of The Employee To Be Deleted */ def delete(id: Long) {}

    }

    Now we have our :- Controller MyOfficeController.scala- Model Employee.scala Lets write Application template employee.scala.html.

    The employee form :Form object encapsulates an HTML form definition, including validation constraints. Lets create a very simple form in the Application controller: we only need a form with a single name field. The form will also check that the name provided by the user is not empty.

    The employee form in MyOfficeController.scala

    val employeeForm = Form( "name" -> nonEmptyText)

    The employee form in MyOfficeController.scala

    The employee form in MyOfficeController.scala

    The type of employeeForm is the Form[String] since it is a form generating a simple String. You also need to Import some play.api.data classes.

    @(employees: List[Employee], employeeForm: Form[String])

    @import helper._

    @main("My Office") { Presently @employees.size employee(s)

    • @employees.map { employee =>
    • @employee.name @form(routes.MyOfficeController.deleteEmployee(employee.id)) { } }

    Add a new Employee @form(routes.MyOfficeController.newEmployee) { @inputText(employeeForm("name")) } }

    Rendering the employees.scala.html page

    Assign the work to the controller method employees.

    /** * Total Employees In Office */ def employees = Action { Ok(views.html.employee(Employee.allEmployees(), employeeForm)) }

    Now hit the http://localhost:9000/employees and see what happens.

    Form Submission

    For now, if we submit the employee creation form, we still get theTODO page. Lets write the implementation of the newEmployeeaction :

    def newEmployee = Action { implicit request => employeeForm.bindFromRequest.fold( errors => BadRequest(views.html.employee(Employee.allEmployees(), employeeForm)), name => { Employee.newEmployee(name) Redirect(routes.MyOfficeController.employees) }) }

    Persist the employees in a database

    Its now time to persist the employees in a database to make the application useful. Lets start by enabling a database in our application. In the conf/application.conf file, add:

    db.default.driver=org.h2.Driverdb.default.url="jdbc:h2:mem:play"

    For now we will use a simple in memory database using H2. No need to restart the server, refreshing the browser isenough to set up the database.

    We will use Anorm in this tutorial to query the database. First we need to define the database schema. Lets use Playevolutions for that, so create a first evolution script in conf/evolutions/default/1.sql:

    # Employees schema # --- !Ups

    CREATE SEQUENCE employee_id_seq;CREATE TABLE employee ( id integer NOT NULL DEFAULT nextval('employee_id_seq'), name varchar(255)); # --- !Downs DROP TABLE employee;DROP SEQUENCE employee_id_seq;

    Now if you refresh your browser, Play will warn you that your database needs evolution:

    Its now time to implement the SQL queries in the Employee companion object, starting with the allEmployees() operation. Using Anorm we can define a parser that will transform a JDBC ResultSet row to a Employee value:

    import anorm._import anorm.SqlParser._

    val employee = { get[Long]("id") ~ get[String]("name") map { case id ~ name => Employee(id, name) } }

    Here, employee is a parser that, given a JDBC ResultSet row with at least an id and a name column, is able to create a Employee value.

    Let us write the implementation of our methods in Employee model.

    /** * Add A New Employee */ def newEmployee = Action { implicit request => employeeForm.bindFromRequest.fold( errors => BadRequest(views.html.employee(Employee.allEmployees(), employeeForm)), name => { Employee.newEmployee(name) Redirect(routes.MyOfficeController.employees) }) } /** * Remove An Employee */ def deleteEmployee(id: Long) = Action { Employee.delete(id) Redirect(routes.MyOfficeController.employees) }

    The model methods

    /** * All Employees In Office */ def allEmployees(): List[Employee] = DB.withConnection { implicit c => SQL("select * from employee").as(employee *) }

    /** * Adding A New Employee */ def newEmployee(nameOfEmployee: String) { DB.withConnection { implicit c => SQL("insert into employee (name) values ({name})").on( 'name -> nameOfEmployee).executeUpdate() } }

    /** * Delete Employee * @param id : id Of The Employee To Be Deleted */ def delete(id: Long) { DB.withConnection { implicit c => SQL("delete from employee where id = {id}").on( 'id -> id).executeUpdate() } }

    That's it

    Let us use the Application MyOffice

    Lets deploy the

    App to

    Heroku needs a file named Procfile for each application to be deployed on it.

    What is Procfile ?

    Procfile is a mechanism for declaring what commands are run when your web or worker dynos are run on the Heroku platform.

    Procfile content :

    web:target/start -Dhttp.port=$PORT -DapplyEvolutions.default=true

    Lets Deploy our application

    1. Create a file Procfile and put it in the root of your project directory.2. Initiate the git session for your project and add files to git as

    git init

    git add .

    3. Commit the changes for the project to git as git commit -m init

    4. On the command line create a new application using the cedar stack: heroku create -s cedar5. Add the git remote : git remote add heroku //TheCloneUrlOnTheHerokuApp//

    6. Application is ready to be deployed to the cloud. push the project files to heroku as: git push heroku master

    Go to your heroku account -> MyApps and youll find a new application that you had just created. Launch the URL and have a look to your running application on heroku. You can also rename your application.