Webページを作ってみる
- 素数探索、フィボナッチ数列でなんとなく制御構文の要領は掴めてきた
- オブジェクト、Enum、ファイルI/O…etcを学ぶためにTODOアプリを作ってみる
- Play Framework をベースにする(業務ではSpring を使っているので、対抗馬?的な意味もこめて)
- 今回の記事では「サンプルデータをブラウザに表示する」まで
Play Framework 導入
以下のサンプルアプリを
git clone
github.comIntelliJ の Run/Debug Configurations から tasks に
run
を追加
で起動できます。素晴らしいスピード感!このサンプルアプリをコピペ参考にして開発を進めます。
TODOアプリ
- TODOの保存はファイルベースで行う
- ファイルI/Oの練習も兼ねて
- 早く画面を出したかった
DBの設定でハマるのに怯えた
- 一旦、項目としては以下の3つ
- id
- ステータス(Enum….みたいな感じ)
- タイトル
ソース
Controller
- TodoController
package controllers import javax.inject._ import play.api.mvc._ import services.TodoManager /** * controller class for todo management */ @Singleton class TodoController @Inject()(todoManager: TodoManager, cc: ControllerComponents) extends AbstractController(cc) { /** * returns view of todo-list */ def list = Action { Ok(views.html.todo(todoManager.list)) } }
サービスクラスをInject
して返り値をhtmlにセットして返すイメージ。
Domain
- Todo
package services.domain /** * entity class of todo */ class Todo(id: Int, status: TodoStatus, title: String) { def getId = id def getStatus = status def getTitle = title }
POJO(Javaじゃないけど)はこんな感じで書ける。immutable に保つためにセッターは用意しない。
- TodoStatus
package services.domain /** * status of todo */ sealed trait TodoStatus case object UNDONE extends TodoStatus case object DOING extends TodoStatus case object DONE extends TodoStatus
Enum は sealed trait
を用意してそれを継承したクラスを列挙する。trait
は Java でいう interface
みたいなイメージ。
Service
- TodoManager
package services import com.google.inject.Singleton import services.domain._ import scala.collection.{immutable, mutable} import scala.io.Source /** * service class for todo management */ @Singleton class TodoManager { private def initializeData(): mutable.Map[Int, Todo] = { val map: mutable.Map[Int, Todo] = new mutable.HashMap[Int, Todo] val s = Source.fromFile("C:\\data\\TODO.txt") val strList: List[String] = try s.getLines.toList finally s.close() strList.foreach(str => { val data: Array[String] = str split "\t" val todo: Todo = new Todo(data(0).toInt, statusMap.get(data(1)).get, data(2)) map.put(todo.getId, todo) }) return map } private val statusMap: immutable.Map[String, TodoStatus] = Map("DONE" -> DONE, "DOING" -> DOING, "UNDONE" -> UNDONE) private val todoMap: mutable.Map[Int, Todo] = initializeData() // read file at startup /** * @return todo-list order by id */ def list = todoMap.values.toList.sortBy(t => t.getId) }
起動時にファイルを読み込んで map にして保持するところまで。 ファイルの読み込み→クローズが サラッと綺麗にできるのが嬉しい。
val s = Source.fromFile("C:\\data\\TODO.txt") val strList: List[String] = try s.getLines.toList finally s.close()
View
- todo.scala.html
@import services.domain.Todo @(todo_list: List[Todo]) <head> <title>todoList</title> </head> <h1>Todo List</h1> <table> @for(todo <- todo_list) { <tr> <td> @todo.getId</td> <td> @todo.getStatus</td> <td> @todo.getTitle</td> </tr> } <table/>
テンプレートエンジンTwirl
が使いやすい。@
をつけて Scala を書き始められるのがメチャメチャ楽!
thymeleaf
より使いやすい説ある。ちなみに読み方は多分"トゥヮル"って感じだと思ってます。
DI
- Modules.scala
class Module extends AbstractModule { override def configure() = { // TodoManager bind(classOf[TodoManager]).asEagerSingleton() } }
業務でも使い慣れているgoogleのguice
だったので、特に違和感ないです。
動作確認
こんな感じ
うん、ださい。とりあえずBootstrapくらいは入れよう。
感想
- Play frameworkは、総じて素直というか、直感的に開発できるようになってるなぁという印象。
Spring の黒魔術も嫌いじゃないし、とてもお世話になっているんだけど。 Twirl
がほんとに使いやすくて感動した。- 言うても Java というか、オブジェクト指向なんだなというのも率直な感想。
関数的に書くのは、当然それなりのメリットは有るんだろうけど、モノ作るならオブジェクト指向が楽な気がする。
今後の課題とか
- ファイルと言うか、データ管理は
repository
クラスを切り出しておきたい - UIはもうちょい綺麗にする、頑張ってスタイルシートを書く
- 新規TODO追加、ステータス更新機能追加
- 検索、フィルタ機能追加(
repository
でやるかクライアントでやるかは検討) - DBでのデータ管理
こんな感じで。今週は割りと頑張りました。