Scala.js を情報商材風に説明する

天下一altJS武闘会Scala.js の紹介をしました。

Scala.js 楽しいよって言っても全く流行る気配ないので、 情報商材風のスライドを作ってみました。洗脳されちゃって下さい。

f:id:tototoshi:20140608174751p:plain

http://tototoshi.github.io/slides/tenka1altjs-scalajs/

fork元を明示したいところですが、 普通によくある怪しげな情報商材サイトなのでやめておきます。

Play 2.4 と Dependency Injection

Play 2.3 が出たばっかで 2.4 の話をします。

前置き: Scala での DI

Scala では DI についてのベストプラクティスと言える方法はなく、まだ意見が分かれている状態です。大きく割ると DI コンテナなどを使った動的な DI と、cake pattern, implicit parameter, macro, reader monad などを利用した静的な DI の 2 パターンです。

動的な DI と静的な DI にはそれぞれメリットとデメリットがあります。動的な DI のメリットは Java のわりと優秀な DI コンテナ (Guice とか) が使えて比較的取っ付きやすいこと、デメリットは型安全ではないところ。静的な DI のメリットは型安全、デメリットは trait だの macro だの implicit parameter だの、コンパイル時間が伸びる要因満載であることです。

また静的な DI を行うとコードが少し複雑になってしまう傾向もあります。私は良く cake pattern で DI をしていますが、いくつもの trait を組み合わせてオブジェクトを組み立てていくと実装がばらけてしまい、コードが追いづらくなるという批判は納得しています。オブジェクトの継承関係が複雑になると、初期化順でハマったりもします。implicit parameter や reader monad による DI はあまりしませんが、まあ多くの開発者はそんなフレンドリーとは受け取ってくれないでしょう。

型安全好きな Scala コミュニティとしては当然静的な DI が良いよねと言いたいんでしょうが、現状はいろいろ問題があるので、DI は定期的によく盛り上がる(燃え上がる)話題の一つです。

Play 2.4 の DI

Play には DI の機能は一部あるものの、まだ本気ではない感じです。 そんな中 play-framework dev(開発者 ML) に、James Roper さんから DI の話題が投下されました。

Play 2.4 - Dependency Injection

要旨をまとめると

  • Play としては DI を動的に行うのか、静的に行うのか、どちらか一方の立場を取るつもりはない。ただしドキュメントに載せるなら動的な方法が良い気がする。
  • Play 2.4、そして Play 3.0 では constructor injection を基本にしたい
  • play.api.Play.current が状態を持たせるよりも constructor injection を基本にしたほうがテスタビリティは上がるだろう

constructor injection と言ってるのはまあこんな感じです。

class MyController(wsClient: WSClient) {
  wsClient.url(...)
}

constructor で WSClient という依存性を渡しています。まあ普通っすね。普通っぽいですが、これができると Play ではかなりテストがしやすくなります。

現状では Play の各モジュールは play.api.Application というグローバルな状態を持つクラスにべったりと依存しています。ピンとこない人に説明すると、良く何も考えずに import しているあれです。

import play.api.Play.current // これが play.api.Application のインスタンス

設定などアプリケーションの情報はこのインスタンスがなんでもかんでも持っています。そのため、Play のテストではこの Play.api.Application の Fake を作ってあげる必要があります。サードバーティプラグインを使ったり、データベースの設定を変えたり、ちょっと凝ったことをやりたくなるとこれがなかなかめんどくさい。

// Play のドキュメントから
class ExampleSpec extends PlaySpec with OneAppPerSuite {

  // Override app if you need a FakeApplication with other than
  // default parameters.
  implicit override lazy val app: FakeApplication =
    FakeApplication(
      additionalConfiguration = Map("ehcacheplugin" -> "disabled")
    )

  "The OneAppPerSuite trait" must {
    "provide a FakeApplication" in {
      app.configuration.getString("ehcacheplugin") mustBe Some("disabled")
    }
    "start the FakeApplication" in {
      Play.maybeApplication mustBe Some(app)
    }
  }
}

グローバルなオブジェクトに状態を持たせるのは、良くないなあと思いつつも、実際 Web アプリの場合はこういうグローバルなものがあったほうがやっぱり便利かなと思ってしまうこともあるのでよくわかりません。ただそういう設計ではやっぱりテストしづらいよね。もっとシンプルな Constructor Injection を基本にしたフレームワークにしてテストをもう少し楽に書けるようにしようね、というのが Play 2.4 そして Play 3.0 の方針だそうです。

実際どのような DI の方式が採用されるのか、どのくらいの規模の変更になるのかは知りませんが、Play 2.4、Play 3.0 のことを考えるならあまり play.api.Application(play.api.Play.current) に頼ったコードは書かないよう気をつけていたほうが移行が楽になるかもしれません。

いやあしかしすでにもう Play 3.0 の話題が出る時期なんですね。

middleman で簡単なドキュメントサイトを作る

play-flyway のドキュメントサイトを作るのに middleman を使った。そのメモ。

インストール

$ gem install middleman

最低限のコマンド

middleman init でひな形を作成。

$ middleman init website

...

      create  website/.gitignore
      create  website/config.rb
      create  website/source/index.html.erb
      create  website/source/layouts/layout.erb
      create  website/source/stylesheets
      create  website/source/stylesheets/all.css
      create  website/source/stylesheets/normalize.css
      create  website/source/javascripts
      create  website/source/javascripts/all.js
      create  website/source/images
      create  website/source/images/background.png
      create  website/source/images/middleman.png

source/ 以下のファイルを編集していくことになる。

開発用サーバの起動は middleman server で。

$ bundle exec middleman server

できあがったら middleman build でHTML にエキスポートできる。

$ bundle exec middleman build

Markdown を使えるようにする

redcarpet を Gemfile に加える。オプション次第で GFM っぽくなる。

# Gemfile
gem "redcarpet"
# config.rb
set :markdown_engine, :redcarpet
set :markdown, :tables => true, :autolink => true, :gh_blockcode => true, :fenced_code_blocks => true

これでファイルの拡張子を .markdown にすると markdown で書けるようになる。

シンタックスハイライト

middleman-syntax を使う

# Gemfile
gem "middleman-syntax"
# config.rb
activate :syntax

感想

  • reloadとか勝手にしてくれるし使いやすかった。
  • middleman 流の asset の扱いがよくわかってない、というかまじめに調べてない。layout.erb で普通に script タグ書いたりしたけど、まあいっかという感じ。

play-flyway 1.1.0 をリリースしました

tototoshi/play-flyway

Play 2.3 用に play-flyway 1.1.0 をリリースしました。 pull-req 下さった皆様ありがとうございました。

変更点

Flyway 3.0 への更新は API 的にはさほど変化なく、package 名の変更くらいで済みました。

管理ページに bootstrap を当ててややかっこ良くしました。

f:id:tototoshi:20140601164120p:plain

Play 2.3 がリリースされたので変更点と試し方を説明します

http://www.playframework.com/documentation/2.3.x/Highlights23 https://groups.google.com/forum/#!msg/play-framework/bTvJbeR_zvU/J3reqk6Xo4AJ

変更点は以下の通り

  1. 新規プロジェクトを作るのに今までは play new コマンドを使っていたけれど Typesafe Activator を使うようになった。
  2. asset のコンパイルに sbt-web を利用するようになった(sbt-web については http://slides.pab-tech.net/sbt-web/ を見るとよい) 。
  3. Java8 サポート。Java 6,7 も引き続き使えます。
  4. Play-Java のパフォーマンス向上
  5. Scala 2.11 サポート (2.10 も引き続きサポート)
  6. Anorm の機能追加。SQL Interpolation 使えたりするようになった。
  7. WS の機能追加。
  8. ScalaTemplate の実装が twirl になった。
  9. WebSocket 用の Actor を追加 http://www.playframework.com/documentation/2.3.x/ScalaWebSockets
  10. HTTPS の SSLEngine がカスタマイズできるようになった。
  11. Asset のパフォーマンス向上。
  12. deprecated になっていた古い Result 系の型が削除された。
  13. いろいろバグ直した

以前からバリバリ使ってるよって人的には Result 型の整理がちょっとめんどいかもしれません。あとは WebSocket が Actor で少し楽に書けるようになったのが嬉しいかもしれませんね。Iteratee わけわからんって人も Actor ならまだなんとかなるんじゃないでしょうか。

ライブラリ・プラグイン作者にとってはまず sbt-web が大きくて、asset 系のプラグインを作りたいなら知っておく必要があります。あとは Scala 2.10, 2.11 のクロスビルドになってしまったので、まあ、ビルド頑張りましょう。

Play 初心者の人はプロジェクトの作成は play new ではなく、activator new を使うということだけ抑えておきましょう。古い情報を見て play new を探さないように。また activator のドキュメントを見ると activator ui というコマンドがありますが、activator ui は別に知らなくても良いただの余興みたいなものですのでとにかく気にせずに activator new しましょう。

activator は typesafe のサイトから手に入りますが、林檎教徒であれば homebrew でインストールできます。

$ brew install typesafe-activator

異教徒の方、こちらです。

$ wget http://downloads.typesafe.com/typesafe-activator/1.2.0/typesafe-activator-1.2.0-minimal.zip
$ # いい感じでパスを通す

これで activator コマンドがインストールされるので activator new しましょう。

$ activator new

プロジェクトの名前を打ちます。今回は適当に try-play-23 にしました。

Enter an application name
> try-play-23

Fetching the latest list of templates...


The new application will be created in /Users/toshi/tmp/try-play-23

Browse the list of templates: http://typesafe.com/activator/templates
Enter a template name, or hit tab to see a list

なんかサイト見ろって言ってますが、気にせず tab を押します。

>
Display all 125 possibilities? (y or n)
PlayStartApp
activator-akka-cassandra
activator-akka-scala-guice
activator-akka-spray
activator-akka-tracing
activator-gilt-app

...

なんかテンプレートがいっぱい出てくるんですけど使用するのは play-scala です。

> play-scala
OK, application "try-play-23" is being created using the "play-scala" template.

To run "try-play-23" from the command-line, run:
/Users/toshi/tmp/try-play-23/activator run

To run the test for "try-play-23" from the command-line, run:
/Users/toshi/tmp/try-play-23/activator test

To run the Activator UI for "try-play-23" from the command-line, run:
/Users/toshi/tmp/try-play-23/activator ui

これで try-play-23 プロジェクトができました。activator 使えってうるさいですね。どれどれ。

$ cd try-play-23
$ ls
LICENSE                    conf
README                     logs
activator                  project
activator-launch-1.1.0.jar public
app                        target
build.sbt                  test

プロジェクトの中に activator コマンドと起動用の jar が入っています。2.2 以前は play コマンドを使っていましたが、2.3 からはこの activator コマンドを使うのが流儀のようです。

$ ./activator run

実際 activator は sbt のラッパーなので、あまり必要性を感じない人は rm しちゃって sbt そのまま使えば良いでしょう。

$ rm activator* # 汚物は消毒だ!
$ sbt run

これでもりもりとダウンロードやらコンパイルが始まるので待ちましょう。途中で OutOfMemory とか PermGen なんちゃらとか言われて落ちてもくじけない。

f:id:tototoshi:20140531151458p:plain

ふう、やっと起動した。

感想

  • activator の存在意義は未だにわからない
  • でも play で採用されることで activator の存在がいよいよ知れ渡ってしまったので一応知っておく必要はある
  • sbt にプロジェクトテンプレート作成機能があればいい気がした
  • play new したいがために Play のでかい zip を落としてくる必要がなくなったのは嬉しい

Play で Scala.js を使う

Play meetup では Play や Scalaフレームワークの歴史みたいな話をしていましたが、本当はこのネタで LT をするつもりでした。諸事情によりってやつです。

さて Play で Scala.js をサポートしようぜって話は少し前からありますが、今のところまだ進展はないようです。

Add buildin ScalaJs support · Issue #2321 · playframework/playframework

ただ、もちろん sbt をいじれば一緒に使うことが可能です。

単純に optimizeJS して出力された js を Play の public/javascripts フォルダにコピーするとか。

lazy val copyJS = Command.command("copyJS") { (state) =>
  scala.sys.process.Process(List("cp", "js/target/scala-2.10/scala-js-slide-js-opt.js", "server/public/javascripts/")).!
  state
}

ちょっとやっつけ感ありますが問題なく使えます。

でもまあもうちょっとまともな方法はが公式サイトからリンク貼られてました。 これを clone するか設定パクればOKです。

git clone git@github.com:vmunier/play-with-scalajs-example.git

このプロジェクトでは Scala.js のプロジェクトが scalajs というサブプロジェクト、Play アプリが scalajvm というサブプロジェクト、さらにその両方で共有されるコードが shared というサブプロジェクトになっています。

├── README.md
├── project
│   ├── Build.scala
│   ├── build.properties
│   ├── plugins.sbt
│   ├── project
│   └── target
├── scala
│   ├── shared
│   └── target
├── scalajs
│   ├── src
│   └── target
└── scalajvm
    ├── app
    ├── conf
    ├── logs
    ├── public
    ├── target
    └── test

このやり方については pab_tech さんも書いてるのでそちらを見てください。

Scala.jsとJVMの両対応コードとScala.jsのテストの書き方 | PABlog

で、Play と使うための build.sbt 設定の中心は以下の部分です。 Scala.js の出力先を Play の public/javascripts にしています。さらに dependsOn を使って Play のアプリをコンパイルしたときに Scala.js のプロジェクトのコンパイルを走らせるようにしています。dist コマンドを使ったときには optimize も行うようになっています。

lazy val scalajvmSettings =
    play.Project.playScalaSettings ++ Seq(
      scalajsOutputDir     := (crossTarget in Compile).value / "classes" / "public" / "javascripts",
      compile in Compile <<= (compile in Compile) dependsOn (preoptimizeJS in (scalajs, Compile)),
      dist <<= dist dependsOn (optimizeJS in (scalajs, Compile)),
    ) ++ (
      // ask scalajs project to put its outputs in scalajsOutputDir
      Seq(packageExternalDepsJS, packageInternalDepsJS, packageExportedProductsJS, preoptimizeJS, optimizeJS) map { packageJSKey =>
        crossTarget in (scalajs, Compile, packageJSKey) := scalajsOutputDir.value
      }
    )

これで、Play でも Scala.js が使えるようになります。やったー!コンパイル時間がさらに伸びますね!

f:id:tototoshi:20140525232911p:plain

Play 2 meetup を開催しました & スライドまとめ

ドワンゴさんを会場としてお借りして Play meetup を行いました。ありがとうございました!

f:id:tototoshi:20140525010622p:plain

http://connpass.com/event/6020/

発表資料をTLから拾ってまとめましたのでどうぞ。

Play と Scala のこれまでとこれから/@tototoshi

(開会挨拶的なもので特に面白くはないですが一応貼っておきます)

やさしい Iteratee 入門 / @kawachi

play2のjsonと型クラス @xuwei_k

f:id:tototoshi:20140525010334p:plain

http://xuwei-k.github.io/slides/play2typeclasses/

DSLからのコードジェネレーションで楽々Play開発 @takezoux2

ActionBuilder の罠(仮) @gakuzzzz

f:id:tototoshi:20140525010338p:plain

https://docs.google.com/presentation/d/1jzdBxSz-gSUxOG16WDguJlu1i4y8CE_-vbCaqgZjWkY/present#slide=id.p

sbt-webを使ったプラグインの作り方(仮) @pab_tech

f:id:tototoshi:20140525010336p:plain

http://slides.pab-tech.net/sbt-web/

フロントエンドとバックエンドのビルド構成について @kara_d

一番簡単なWebSocketの試し方 @shunjikonishi

Play2 WS 再入門 @magnet88jp

f:id:tototoshi:20140525010339p:plain

https://docs.google.com/presentation/d/1H8PhVmMqDHf8bRhsuw03KW0jd98SWKRoEnwt1UecRtc/htmlpresent

MyFleetGirls @ponkotuy

https://gist.github.com/ponkotuy/02c091b00a9317bd8ee3