class: center, middle # SAStruts + S2JDBC から Skinny Framework への移行ガイド Kazuhiro Sera (@seratch) --- ## 自己紹介 - Scala 好き - 2010 年くらいから Scala をやっています - 普段は Scala ばかりではなく Java + Lombok、Kotlin などが多いです - [ScalikeJDBC](http://scalikejdbc.org/) 、[Skinny Framework](http://skinny-framework.org/) など OSS 開発 - Seasar2 ユーザ - かつて SAStruts、S2JDBC、Slim3 など仕事でかなり使っていました - [SAStruts + S2JDBC の思い出について存分に語る](http://qiita.com/seratch@github/items/826f1b4a89993d3b0804) - 昨年は海外での普及を目指した OSS 開発について[話しました](https://event.seasarfoundation.org/sc2015/2015/09/17/seratch/) - その後 SmartNews に入社、Java でサーバサイド開発をやっています - [11 月にサンフランシスコで登壇予定](http://scala.bythebay.io/speakers.html)など今もチャレンジ中です --- ## SAStruts + S2JDBC の移行先? - 普通に考えて [Spring Boot](https://projects.spring.io/spring-boot/) - ニーズに合うなら先のセッションの通り [LastaFlute](http://dbflute.seasar.org/ja/lastaflute/) も - それは前提として Spring Boot もいいけど Scala もあるよという話です - これから Scala で|もやっていこうというのもそれはそれで良い選択 - 現にヌーラボさんなど Scala へ移った S2 ユーザ企業も - 微力ながらご支援もできます (good-flow.com) - 最近 Spring で話題の Reactive は Scala 発祥 - 実務で使わないとしても追いかけてみてもよいのではないかと --- ## ./skinny run で起動 そろそろ話を聴いているだけは飽きてきた頃? デモアプリ、以下を実行して、動かしてみてください。 https://github.com/seratch/sa-struts-to-skinny ``` git clone https://github.com/seratch/sa-struts-to-skinny.git cd sa-struts-to-skinny/skinny-tutorial ./skinny run ``` http://localhost:8080/ にアクセスすると、懐かしいアレが・・! --- ## SAStruts Tutorial in Scala  --- ## 本家を mvn jetty:run 別 port で本家も起動できるようにしておいたので見比べてみてください。 ``` cd sa-struts-to-skinny/sa-struts-tutorial mvn jetty:run open http://localhost:8081/ ``` SAStruts Tutorial を使った移行例については後半詳しく触れます。 --- ## ちょっとした年表 |年|起きたこと| |---|---| |2005|Ruby on Rails 1.0| |2007|「[Java から Ruby へ](https://www.oreilly.co.jp/books/9784873113203/)」出版| |2008|SAStruts 1.0| |2010|Slim3 for GAE/J 1.0 (-2012)| |2012|Play Framework 2.0| --- ## Web サーバサイド開発 - 201x 年代、すでにサーバサイド開発で Rails 的なものがコモディティ化 - 私自身: S2 休止後は Rails、Jersey、Spring MVC などを使っていた - Scala での Web サーバサイド - 一昔前は [Lift](http://www.liftweb.net/) が有名だった - 2012 年 Java だった Play が突如 v2 から Scala になった - Play 1 は Java on Rails だった - Play 2 はある程度 Rails っぽい見た目の別もの --- ## Scala - Scala (= Scalable language) - JVM 言語の一つ, 2004 年に公開 - Object-Oriented Meets Functional - [公式サイト](http://scala-lang.org/) - 今年 [Scala Center](https://scala.epfl.ch/) 発足 - Lightbend(旧 Typesafe)と連携してエコシステムの拡大 - IBM, Verizon, Goldman Sachs 等の他の企業もスポンサードする体制 - 今では Twitter だけでなく多くの企業が使っている - [Lightbend 社 case study](http://www.lightbend.com/resources/case-studies-and-stories) - Twitter、Walmart、Intel、SAMSUNG、LinkedIn、Coursera、The Guardian、Databricks、The Huffington Post、airbnb、etc.. - ドワンゴさんでは[新卒教育で Scala エンジニアに](https://dwango.github.io/scala_text/) --- ## Skinny - 非同期処理を前提としない Scala - Future が標準 API になったのは割と最近(2.10) - それ以外の用途にも使える言語だったはず - Java コードを流用する > 全てを Scala らしく書く - Scala on Rails - 普通の CRUD 画面的なものを手軽に作る手段がない - 界隈にそれ**も**欲している人は少なくないように見えた - それがハマるときは scaffold とかしたい - 2013 年秋頃着手し、2014 年春に Skinny 1.0 release - [コンセプトのドラフト文書](https://gist.github.com/seratch/7382298) - [Skinny Framework Meetup Tokyo 1](https://gist.github.com/seratch/b61f0ae71cb5b5aa5b78) --- class: center, middle # Demo1: New Skinny Project --- ## Demo 1: Installation - JDK7 以上だけあれば OK - Mac なら `brew install skinny` - Linux や Windows は、[公式サイト](http://skinny-framework.org/)にある skinny-blank-app-with-deps.zip を解凍  --- ## Installation Tips - Eclipse は厳しいので IntelliJ IDEA を使うとよいです - https://www.jetbrains.com/idea/ - Scala は Community Edition でも始められます - Scala プラグインを入れてください - IDEA はマシンパワーを必要とするので Atom + ENSIME もオススメ - https://gist.github.com/seratch/cc472f64f72ba725360ee6517008e23c - https://twitter.com/k_kato/status/777679479388774400 - Scala を始めてみようとすると sbt の依存解決が遅い - [なぜあなたの sbt はすぐに起動しないのか](http://qiita.com/seratch@github/items/243e99117429bda5f172) - 以下の sbt プラグインを追加すると劇的に改善します - https://github.com/alexarchambault/coursier ``` addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-M14") ``` --- ## Demo 1: New Project ``` skinny new hello-world cd hello-world git init git add . -v git commit -m'Initial' skinny run ``` http://localhost:8080/ にアクセスする --- ## IDE Settings IntelliJ IDEA ならプロジェクトのディレクトリを指定して開くだけ。 Eclipse は・・これを機に IntelliJ 使ってみてください。 Atom / emacs なら ENSIME の設定は以下をたたいて .ensime をつくるだけ。 (ENSIME の sbt プラグインが必要) ``` sbt ensimeConfig ``` https://gist.github.com/seratch/cc472f64f72ba725360ee6517008e23c --- ## Demo 1: New Controller controller のコードとルーティング設定を自動生成 ``` ./skinny g controller hello ``` 以下のようにファイルが生成されます。 ``` [info] Running TaskRunner generate:controller hello *** Skinny Generator Task *** "src/main/scala/controller/ApplicationController.scala" skipped. "src/main/scala/controller/HelloController.scala" created. "src/main/scala/controller/Controllers.scala" modified. "src/test/scala/controller/HelloControllerSpec.scala" created. "src/test/scala/integrationtest/HelloController_IntegrationTestSpec.scala" created. ``` --- ## Demo 1: New Controller ``` class HelloController extends ApplicationController { protectFromForgery() def index = render("/hello/index") } ``` dev モードで http://localhost:8080/hello にアクセスすると未作成の view(/hello/index)が自動生成されます。 その `src/main/webapp/WEB-INF/views/hello/index.html.ssp` を編集して「Hello World」を表示させましょう。 ``` git add . -v git commit -m'Add /hello' ``` --- ## Demo 1: Scaffold ``` ./skinny g scaffold tasks task \ title:String \ description:Option[String] \ deadline:Option[LocalDate] ``` 以下のようにファイルが生成されます。MVC ソースコード、ルーティング設定、DB マイグレーション、国際化用メッセージとテストコードです。 ``` *** Skinny Generator Task *** "src/main/resources/messages.conf" modified. "src/main/resources/db/migration/V20160924084655__Create_tasks_table.sql" created. "src/main/scala/controller/TasksController.scala" created. "src/main/scala/controller/Controllers.scala" modified. "src/main/scala/model/Task.scala" created. "src/main/webapp/WEB-INF/views/tasks/_form.html.ssp" created. "src/main/webapp/WEB-INF/views/tasks/new.html.ssp" created. "src/main/webapp/WEB-INF/views/tasks/edit.html.ssp" created. "src/main/webapp/WEB-INF/views/tasks/index.html.ssp" created. "src/main/webapp/WEB-INF/views/tasks/show.html.ssp" created. "src/test/resources/factories.conf" modified. "src/test/scala/controller/TasksControllerSpec.scala" created. "src/test/scala/integrationtest/TasksController_IntegrationTestSpec.scala" created. "src/test/scala/model/TaskSpec.scala" created. ``` --- ## Demo 1: Scaffold ``` git add . -v git commit -m'Add tasks scaffold' ``` この時点で http://localhost:8080/tasks にアクセスするとまだテーブルがないのでエラーになります。 ``` ./skinny db:migrate ``` db:migrate したあとで http://localhost:8080/tasks にアクセスすると [Bootstrap](http://getbootstrap.com/) ベースの CRUD 画面が自動生成されています。  --- ## Demo 1: Scaffold 入力バリデーションが型に応じて自動生成されるなど、Rails よりも優れている点もあります。 ``` override def createForm = validation(createParams, paramKey("title") is required & maxLength(512), paramKey("description") is maxLength(512), paramKey("deadline") is dateFormat ) override def updateFormStrongParameters = Seq( "title" -> ParamType.String, "description" -> ParamType.String, "deadline" -> ParamType.LocalDate ) ``` override がついている通り scaffold で生成されるコードは差分プログラミングで必要なところだけカスタマイズできるようになっています。 --- ## Demo 1: Running Tests テストの実行は DB を使うなら test env での migration を実行してから。 ``` ./skinny db:migrate test ./skinny test ``` 特定のテストクラスだけ、特定のパッケージだけでの実行もできます。 skinny コマンドは sbt の薄いラッパーなので sbt と連携している状態なら IDEA からも実行できます。 ``` ./skinny testOnly integrationtest.* ``` --- ## 休憩: Skinny ひとめぐり [公式サイト](http://skinny-framework.org/) を見ればどのような機能があるかはわかります。 主要な機能としては - Web MVC (Routes, Form Validation, View Template) - OAuth 連携(Facebook、Google、GitHub、Nulab、Twitter) - Assets(CoffeeScript、LESS、Sass、Scala.js) - メール送信 - 国際化対応 - ORM、FactoryGirl(fixture DSL) - 簡易的な Job Worker 機構 - rake 的なタスクランナー などがあります。Web MVC 部分以外は独立したライブラリになっているので Play アプリなどでも使えます。 --- class: center, middle # Demo2: Reverse Scaffolding --- ## Demo 2: Reverse Scaffolding すでに存在するデータベースからコードを自動生成できます。まず migration ファイルを [Flyway](https://flywaydb.org/) の命名規約に沿って生成。 ``` ./skinny g migration reverse-demo ``` [DDL をコピー](http://skinny-framework.org/documentation/scaffolding.html#reverse-scaffold-generator)して migration ファイルに書き込みます。 ``` pbpaste > src/main/resources/db/migration/*__reverse-demo.sql ``` DB に DDL を反映してテーブルを準備します。 ``` ./skinny db:migrate ``` 以下でデータベースの全てのテーブルを参照してコードを生成します。model だけ生成するコマンドもあります。 ``` ./skinny g reverse-scaffold-all git add . -v && git commit -m'Add reverse-scaffold demo' ``` --- ## Demo 2: FK があれば association も作る 先ほどの公式ドキュメントの例のように外部キーが適切に指定された例であれば association の設定もついた状態で DAO が生成されます。 - belongsTo, hasMany, hasManyThrough, hasOne ... ぜひ現実世界の DB に対して reverse scaffolding してみてください。 以下は [Redmine](http://www.redmine.org/) の MySQL DB から生成した例です。 - https://github.com/seratch/redmine-reverse-scaffold - https://github.com/seratch/redmine-reverse-scaffold/tree/master/src/main/scala/model 一発でコンパイルが通って動作するコードが生成されます。 --- ## Demo 2: REPL Scala は REPL が昔から充実しています。sbt を使えば、依存ライブラリをロードした状態で簡単に REPL を起動できます。 ``` ./skinny console ``` 以下のような DAO のメソッドを実行してみると、レコードが生成されます。Rails console と同じような使い勝手でコードを簡単に試せます。 ``` Organization.createWithAttributes( 'name -> "Seasar Foundation", 'url -> "http://www.seasar.org/") UserGroup.createWithAttributes('name -> "s2buri users") val userGroupId = UserGroup.limit(1).apply().head.id Developer.createWithAttributes( 'name -> "Higa", 'userGroupId -> userGroupId) ``` --- ## Demo 2: REPL 先ほどつくったレコードを select してみる例。 ``` Developer.where('name -> "Higa").apply() Developer.where('name -> "Higa").count() val developers = Developer.limit(3).apply() val developers = Developer.joins(Developer.userGroupRef).findAll() skinny.json4s.JSONStringOps.toJSONString(developers) ``` --- class: center, middle # Demo3: SAStruts Tutorial --- ## Demo 3: SAStruts Tutorial 先ほど動かしてみようと紹介したもの。あらかじめつくっておきました。 https://github.com/seratch/sa-struts-to-skinny Struts の client side validation と security-constraint だけスキップしましたが、あとは極力そのまま移植しました。 --- ## Demo 3: SAStruts Tutorial 移植コード 実装の内容は、このへんだけ見ればだいたいわかるようになっています。 Scala 初見でも Java の方なら何となくわかるはず。 - [src/main/scala/controller/Controllers.scala](https://github.com/seratch/sa-struts-to-skinny/blob/master/skinny-tutorial/src/main/scala/controller/Controllers.scala) - [src/main/scala/controller/tutorial](https://github.com/seratch/sa-struts-to-skinny/tree/master/skinny-tutorial/src/main/scala/controller/tutorial) - [src/main/scala/model](https://github.com/seratch/sa-struts-to-skinny/tree/master/skinny-tutorial/src/main/scala/model) - [src/main/webapp/WEB-INF/layouts](https://github.com/seratch/sa-struts-to-skinny/tree/master/skinny-tutorial/src/main/webapp/WEB-INF/layouts) - [src/main/webapp/WEB-INF/views/tutorial](https://github.com/seratch/sa-struts-to-skinny/tree/master/skinny-tutorial/src/main/webapp/WEB-INF/views/tutorial) --- ## Demo 3: SAStruts Tutorial 雑感 - [sa.css](https://github.com/seratch/sa-struts-to-skinny/blob/master/skinny-tutorial/src/main/webapp/assets/css/sa.css) すごくシンプルなんだけど、なんだか味がありますね - SAStruts の良さ - Rails 的な `mapItems[0].id` みたいなパラメータのサポート - submit ボタンの name パラメータの値で規約 dispatch - Scala との比較 - Scala + Skinny の場合、基本的に記述は Java より簡潔 - Scala はアノテーション文化ではない(Play は最近ちょっとある..) - Skinny: view はすぐに反映、それ以外は変更検知での自動再起動 - どうしても Hot reloading が欲しいなら Play にはあります - tutorial 完コピできてない箇所あったら pull request ください・・! --- ## Demo 3: Add (SAStruts) ``` public class AddForm { @Required @IntegerType public String arg1; @Required @IntegerType public String arg2; } public class AddAction { public Integer result; @Resource @ActionForm protected AddForm addForm; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(input = "index.jsp") public String submit() { result = Integer.valueOf(addForm.arg1) + Integer.valueOf(addForm.arg2); return "index.jsp"; } } ``` --- ## Demo 3: Add (Skinny) ``` class AddController extends ApplicationController { def addForm = validation( Params(params), paramKey("arg1") is required & intValue, paramKey("arg2") is required & intValue ) def index = render("/add/index") def submit = { if (addForm.validate()) { val result = params("arg1").toInt + params("arg2").toInt set("result" -> Some(result)) } render("/add/index") } } ``` --- ## Demo 3: Ajax (SAStruts) SAStruts は jsp のファイル名を返すか ResponseUtil で応答します。 ``` public class AjaxAction { @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(validator = false) public String hello() { ResponseUtil.write("こんにちは"); return null; } } ``` --- ## Demo 3: Ajax (Skinny) Skinny はメソッドが任意のものを返すとそれを response body として応答します。 render は HTML/JSON/XML をよしなに判断して Content-Type を設定しつつ String で body を返します(ここでは HTML)。 ``` class AjaxController extends ApplicationController { def index = render("/ajax/index") def hello = "こんにちは" } ``` ActionResult でレスポンス全体を表現したものを組み立てることもできます。 ``` def hello: ActionResult = Ok( body = "こんにちは", headers = Map("X-Response-Time" -> 50) ) ``` --- ## Demo 3: NestedForeach (SAStruts) SAStruts は public field で view に引き渡すものを表現します。 ``` public class NestedForeachAction { public List
>> mapItemsItems = new ArrayList
>>(); @Execute(validator = false) public String index() { for (int i = 0; i < 10; i++) { List
> mapItems = new ArrayList
>(); for (int j = 0; j < 2; j++) { Map
m = new HashMap
(); m.put("id", i * 10 + j); m.put("name", "name" + i + j); mapItems.add(m); } mapItemsItems.add(mapItems); } return "index.jsp"; } } ``` --- ## Demo 3: NestedForeach (Skinny) Skinny は request scope に set する形で引き渡します。Map object の動的な attribute 生成はありません。case class で入れ物をつくってやりとりします。 ``` case class ForeachItem(id: Int, name: String) class NestedForeachController extends ApplicationController { private[this] val itemsSeq: Seq[Seq[ForeachItem]] = { (0 until 10).map { i => (0 until 2).map { j => ForeachItem(i * 10 + j, s"name${i}${j}") } } } def index = { set("itemsSeq" -> itemsSeq) render("/nestedForeach/index") } } ``` --- ## Demo 3: NestedForeach (SAStruts) 説明不要な jsp の例です。 ``` <%@page pageEncoding="UTF-8"%>
Tutorial: Nested Foreach
${f:h(m.id)}
${f:h(m.name)}
``` --- ## Demo 3: NestedForeach (Skinny) [ssp(Scala Server Pages)](https://scalate.github.io/scalate/documentation/ssp-reference.html)の例です。 for 式さえ慣れればあとは Velocity に非常に似ているのですぐに使えると思います。 Play には [twirl](https://www.playframework.com/documentation/2.5.x/ScalaTemplates) という別のテンプレートエンジンがあります。 ``` <%@val itemsSeq: Seq[Seq[model.ForeachItem]] %>
Tutorial: Nested Foreach
#for (items <- itemsSeq)
#for (m <- items)
${m.id}
${m.name}
#end
#end
``` --- ## まとめ - Seasar プロジェクト、本当にお疲れ様でした! - 私は今後も世界展開を目指して頑張ります - OSS も SmartNews も - Scala 実際触ってみると良さがわかる感じがあります - 興味を持った方はこの週末ぜひ試してみてください - 最近は Apache Spark で始める人多いです - Web なら Skinny でも Play でもお好きな方で