scala webアプリ 実践3-チュートリアルを試す
今回は、チュートリアル(localhost:9000からリンク先があります)に従って簡単なアプリケーションを作ってみたいと思います。
TODOリストを作る
プロジェクトを作る
bash-4.2$ play new todolist
起動する
bash-4.2$ cd todolist/
bash-4.2$ play
ideを設定する(前回と同じ設定をintelljにする)
[todolist] $ idea
[todolist] $ run
Application.scalaを編集する
todolist/app/controllers/Application.scalaを編集し、
ブラウザから「http://localhost:9000/」を叩き、helloworldが表示されることを確認する
def index = Action { Ok("Hello world") }
routesをつくりましょ
conf/routesを編集し、routeを追加し、Application.scalaに対応したメソッドをtodoで作成する
ブラウザから「http://localhost:9000/tasks」を叩くと、TODOが表示される。
これは便利!!
# Home page GET / controllers.Application.index # Tasks GET /tasks controllers.Application.tasks POST /tasks controllers.Application.newTask POST /tasks/:id/delete controllers.Application.deleteTask(id: Long)
object Application extends Controller { def index = Action { Ok("helloworld") } def tasks = TODO def newTask = TODO def deleteTask(id: Long) = TODO }
最後に、/tasks にリダイレクトするように変更します
※helloworldだったところです
def index = Action {
Redirect(routes.Application.tasks)
}
タスクのモデルを準備する
app/models/Task.scalaを新規作成する
package models case class Task(id: Long, label: String) object Task { def all(): List[Task] = Nil def create(label: String) {} def delete(id: Long) {} }
※case classとはなにか?
Case classes are regular classes which export their constructor parameters and which provide a recursive decomposition mechanism via pattern matching
つたない感じで訳すと、『コンストラクタのパラメータを出力し、パターンマッチングを経由し、再帰的な分解機能を提供するクラス』だそうです。。ようわかりませんが、いったんふーんと言っておきましょう。
テンプレートを作りましょ
index.scala.htmlを編集します
ここでやっているのは、
- タスクリストの表示
- タスクとのフォーム
@(tasks: List[Task], taskForm: Form[String]) @import helper._ @main("Todo list") { <h1>@tasks.size task(s)</h1> <ul> @tasks.map { task => <li> @task.label @form(routes.Application.deleteTask(task.id)) { <input type="submit" value="Delete"> } </li> } </ul> <h2>Add a new task</h2> @form(routes.Application.newTask) { @inputText(taskForm("label")) <input type="submit" value="Create"> } }
※「helper._」はformの助成をしてくれる
formに、バリデーションとタスクのアクションを指定する
validation部分
val taskForm = Form( "label" -> nonEmptyText )
action追加分
def tasks = Action {
Ok(views.html.index(Task.all(), taskForm))
}
Application.scala 全部
package controllers import play.api._ import play.api.mvc._ import play.api.data._ import play.api.data.Forms._ import models.Task object Application extends Controller { val taskForm = Form( "label" -> nonEmptyText ) def index = Action { Redirect(routes.Application.tasks) } def tasks = Action { Ok(views.html.index(Task.all(), taskForm)) } def newTask = TODO def deleteTask(id: Long) = TODO }
サブミットはハンドリングする
Application.scala のnewTaskのTODO部分の変更
def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Task.all(), errors)),
label => {
Task.create(label)
Redirect(routes.Application.tasks)
}
)
}
これで、入力エラー時に、「400 Bad Request」を返却し、入力エラーがない場合は200を返すようになります。
データベースを設定する
conf/application.conf を変更します。コメントアウトされているのを
外すだけでいけます。
db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play"
※H2とは?
javaで書かれた、軽量データベースエンジンであり、最速との呼び声もあるそうです
定義ファイルを作成する
conf/evolutions/default/1.sql
# Tasks schema # --- !Ups CREATE SEQUENCE task_id_seq; CREATE TABLE task ( id integer NOT NULL DEFAULT nextval('task_id_seq'), label varchar(255) ); # --- !Downs DROP TABLE task; DROP SEQUENCE task_id_seq;
次に、ブラウザから「http://localhost:9000/tasks」にアクセスすると、
「An SQL script will be run on your database - Apply this script now!」
と表示されていて、「Apply this script now!」部分がボタンになっているので、
これをクリックする。
これにより、databaseは有効化され使用可能になる。
定義を読み込みparseし、処理を組み込む
Task.scalaの全文です。
package models import anorm._ import anorm.SqlParser._ import play.api.db._ import play.api.Play.current case class Task(id: Long, label: String) object Task { val task = { get[Long]("id") ~ get[String]("label") map { case id~label => Task(id, label) } } def all(): List[Task] = DB.withConnection { implicit c => SQL("select * from task").as(task *) } def create(label: String) { DB.withConnection { implicit c => SQL("insert into task (label) values ({label})").on( 'label -> label ).executeUpdate() } } def delete(id: Long) { DB.withConnection { implicit c => SQL("delete from task where id = {id}").on( 'id -> id ).executeUpdate() } } }
未実装部分の削除部分のルーティングを編集
Application.scala
def deleteTask(id: Long) = Action { Task.delete(id) Redirect(routes.Application.tasks) }
完成!!
雑感
チュートリアルが優秀なせいか、いつもむだなところで躓くが、
すんなり、db連携までもできてしまった。すげー