2015/6/22

Introduction to Play Framework

Introduction to Play Framework for Scala developers 這個影片,由建立 Play Framework 專案開始,解釋初始專案的內容,在 trace 的過程中,連帶將如何使用 Play Framework 的概念一併提供給讀者。

啟動 play framework web server

在「命令提示字元」裡,切換到 scala-play 的目錄,然後鍵入指令 activator run ,就能啟動 play framework web server。瀏覽器瀏覽網頁 http://localhost:9000/ 就可以看到 play framework 的測試網頁。

網站首頁

  1. 首先我們先看 conf/routes 這個設定檔,這裡面針對 / 這個 URI,設定是由 controllers.Application 這個類別的 index 這個 method 負責處理。

    設定檔 /conf/routes

     ## Home page
     GET     /                           controllers.Application.index
    

    在 app/controllers/Application.scala 這個 controller 中,會看到處理 index 的 Action

     package controllers
    
     import play.api._
     import play.api.mvc._
    
     class Application extends Controller {
    
       def index = Action {
         Ok(views.html.index("Your new application is ready."))
       }
    
     }
    

    瀏覽器首頁最上面的 "Your new application is ready." 文字,就是從這裡列印到網頁中的,但實際上,Ok 裡面還是呼叫了 view template 的程式,後面的部份是傳送給 view template 的文字參數。

    在 play framework web server 啟動的狀態下,如果我們修改了 app/controllers/Application.scala 的內容,網頁會自動重新編譯,並輸出到瀏覽器上。同樣地,當原始程式編譯產生錯誤時,網頁也會自動顯示錯誤點,而且未來在實際上線時,可以將顯示錯誤點網頁的功能關閉。

  2. view template
    app/views/index.scala.html 最前面的 message 是收到的參數內容,也就是上一個步驟傳送進來的 "Your new application is ready.",接下來的 template 引用了兩個參數,並傳送至 app/views/main.scala.html。

    play20.welcome 是一個內建在 play framework 的 template ,裡面是 play framework 的 document 網頁。

     @(message: String)
    
     @main("Welcome to Play") {
    
         @play20.welcome(message)
    
     }
    

    app/views/main.scala.html 的一開始,定義了兩個參數,分別是 title 以及content html。

     @(title: String)(content: Html)
    
     <!DOCTYPE html>
    
     <html lang="en">
         <head>
             <title>@title</title>
             <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
             <link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
             <script src="@routes.Assets.versioned("javascripts/hello.js")" type="text/javascript"></script>
         </head>
         <body>
             @content
         </body>
     </html>
    

    將 app/views/index.scala.html 裡面的 @play20.welcome(message) 改為 @message,瀏覽器上就只會剩下 "Your new application is ready." 的文字列印在畫面上。

     @(message: String)
    
     @main("Welcome to Play") {
         <!-- @play20.welcome(message) -->
         @message
     }
    

如何測試 play framework

test/ApplicationSpec.scala 是直接發送 http request,然後以 http response code 確認是否正常。

    import org.specs2.mutable._
    import org.specs2.runner._
    import org.junit.runner._

    import play.api.test._
    import play.api.test.Helpers._

    /**
     * Add your spec here.
     * You can mock out a whole application including requests, plugins etc.
     * For more information, consult the wiki.
     */
    @RunWith(classOf[JUnitRunner])
    class ApplicationSpec extends Specification {

      "Application" should {

        "send 404 on a bad request" in new WithApplication{
          route(FakeRequest(GET, "/boum")) must beSome.which (status(_) == NOT_FOUND)
        }

        "render the index page" in new WithApplication{
          val home = route(FakeRequest(GET, "/")).get

          status(home) must equalTo(OK)
          contentType(home) must beSome.which(_ == "text/html")
          contentAsString(home) must contain ("Your new application is ready.")
        }
      }
    }

利用 browser 測試網站

    import org.specs2.mutable._
    import org.specs2.runner._
    import org.junit.runner._

    import play.api.test._
    import play.api.test.Helpers._

    /**
     * add your integration spec here.
     * An integration test will fire up a whole play application in a real (or headless) browser
     */
    @RunWith(classOf[JUnitRunner])
    class IntegrationSpec extends Specification {

      "Application" should {

        "work from within a browser" in new WithBrowser {

          browser.goTo("http://localhost:" + port)

          browser.pageSource must contain("Your new application is ready.")
        }
      }
    }

打開一個新的 「命令提示字元」,切換到 project 目錄,輸入 activator test

> activator test
[info] Loading global plugins from C:\Users\yaocl\.sbt\0.13\plugins
[info] Loading project definition from D:\projectcase\trunk3\play-scala-intro\project
[info] Set current project to play-scala-intro (in build file:/D:/projectcase/trunk3/play-scala-intro/)
[info] Compiling 2 Scala sources to D:\projectcase\trunk3\play-scala-intro\targe
t\scala-2.11\test-classes...
[info]
[info] IntegrationSpec
[info]
[info] Application should
[info] - play.api.libs.concurrent.ActorSystemProvider - Starting application default Akka system: application
[info] - play.api.libs.concurrent.ActorSystemProvider - Shutdown application default Akka system: application
[info]   + work from within a browser
[info]
[info] Total for specification IntegrationSpec
[info] Finished in 14 seconds, 734 ms
[info] 1 example, 0 failure, 0 error
[info]
[info] ApplicationSpec
[info]
[info] Application should
[info] - play.api.libs.concurrent.ActorSystemProvider - Starting application default Akka system: application
[info] - play.api.libs.concurrent.ActorSystemProvider - Shutdown application default Akka system: application
[info]   + send 404 on a bad request
[info] - play.api.libs.concurrent.ActorSystemProvider - Starting application default Akka system: application
[info] - play.api.libs.concurrent.ActorSystemProvider - Shutdown application default Akka system: application
[info]   + render the index page
[info]
[info] Total for specification ApplicationSpec
[info] Finished in 447 ms
[info] 2 examples, 0 failure, 0 error
[info]
[error] Failed: Total 3, Failed 0, Errors 0, Passed 3
[error] (test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 90 s, completed 2015/6/11 下午 01:53:07

如果測試時發生錯誤,修正程式後,可直接執行 "activator test-quick" 快速重新測試 failed tests

Data Persistence

在測試影片中說明的 sorm : scala 使用的 ORM framework,一直發生一些問題,在 play framework issue #4342中提到,sorm 原本是以 scala 2.10 編譯的,可能因此沒能正常地支援 2.11,最後建議嘗試使用 Anorm 或是 Slick

根據上述文章的建議,我們就測試了 Anorm 的方案。

activator computer-database-scala 的 project template 設定過程如下:

  1. activate new play-scala-db computer-database-scala
    以 computer-database-scala 這個 template 建立一個新專案 play-scala-db

  2. cd play-scala-db
    切換到 play-scala-db 目錄

  3. activator eclipse
    建立 eclipse project settings

  4. activator run
    啟動 play-scala-db project

  5. 瀏覽器連結到 http://localhost:9000/ 可看到以下的畫面



這個 project template 是個傳統的 CRUD 頁面,Model 利用 Anorm 存取 DB,有使用 twitter bootsctrap 產出頁面以及分頁功能。project 設定檔 conf/application.conf 裡面預設是使用 h2 memory database。