なごやかScala #11 sbtの使い方とScalaによるアプリケーション実装

Table of Contents

1 今回のポイント

  • sbt(Scala Build Tool)は、Scala本体や必要なライブラリのダウンロードをしてくれて便利
  • sbtは、コードの保存を検知して、すぐにテストを実行してくれるので便利
  • Scalaは、Javaの資産もシームレスに使えるので便利
  • REPLは、ちょっとしたコードをすぐ試せるので便利

2 環境の準備

Java JRE 1.6以降が必要です。 プロンプトで「java -version」を実行し、 インストールされているか、パスが通っているか、バージョンは1.6以上か、を確認してください。

~/code/scala/nascala > java -version
java version "1.6.0_37"
Java(TM) SE Runtime Environment (build 1.6.0_37-b06-434-11M3909)
Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01-434, mixed mode)

3 サンプルコードを実行する

※初回起動時はScala本体と依存ライブラリの大量のダウンロードがあります。 心の準備をしてください。(10〜20分くらい?)

  1. サンプルコードの取得
    > git clone https://github.com/maedaunderscore/nascala-FrequentWords.git
    (gitがない場合はhttps://github.com/maedaunderscore/nascala-FrequentWordsからZIPをダウンロード)
    
  2. sbtの起動と実行
    > cd nascala-FrequentWords
    > ./sbt (windowsの場合はsbt.batを実行)
    [info] Loading project definition from /Users/maeda/code/scala/nascala/project
    [info] Set current project to default-04b6c4 (in build file:/Users/maeda/code/scala/nascala/)
    > run
    
  3. 実行すると、ローカルPC上でWebサーバが起動し、ブラウザでそのページを表示します。

3.1 新しいプロジェクトを作成する場合

sbtでは一つのプロジェクトを一つのディレクトリで管理します。1 新しいプロジェクトを作成する場合は下記の流れで始めます。

  1. ディレクトリを作成する
  2. 作成したディレクトリへ移動
  3. sbtを実行

4 サンプルのファイル構成

nascala-FrequentWords
├─ build.sbt                           # sbtの設定ファイル(簡易バージョン)
├─ project                             # sbtの設定ディレクトリ
├─ src                                 # ソースコード
│   ├─ main          
│   │   ├─ scala                     # 本体のコード
│   │   │   ├── Main.scala         #   エントリポイント。Webサーバの起動など
│   │   │   ├── Web.scala          #   Webから情報を取得する
│   │   │   ├── Analyzer.scala     #   形態素解析をする
│   │   │   └── Template.scala     #   HTMLを生成する
│   │   └── resources               # cssなどのリソースファイル
│   ├─ test                           # テストコード
│   └─ script                         # REPLからの使用を想定した便利スクリプト
└─ target                              # ビルドしたものなどが入る

5 sbt command

以下はsbt起動後のプロンプトから使用可能なコマンド例です。

コマンド動作
runmain関数を実行
testテストを実行
test-only nascala.CountingByScalaCheck指定されたテストだけを実行。*などのワイルドカードも使用可能
consoleREPLを起動
reloadsbtの設定を再読み込み
compileコンパイルを実行
set scalacOptions += "-feature"一時的にWarningの詳細を確認するコンパイルオプションを追加2
set scalacOptions += "-deprecation"一時的にWarningの詳細を確認するコンパイルオプションを追加2
set scalaVersion := "2.10.1"一時的にScalaのバージョンを設定
set libraryDependencies += "…" %% "…" % "…"一時的に使用するライブラリを追加
session list一時的に設定したものを確認
session savesession listの内容をbuild.sbtに保存
session remove n (nは数字)session listの内容のn番目を削除

5.1 継続実行する

コマンドの前に'~'(チルダ)をつけると、コマンドを継続実行します。 改行で待ち受け状態をやめて、プロンプトに戻ります。

> ~test
... (テストが実行される) ...
[info] Passed: : Total 9, Failed 0, Errors 0, Passed 9, Skipped 0
[success] Total time: 2 s, completed 2013/03/31 15:35:49
1. Waiting for source changes... (press enter to interrupt)
... (コードの変更待ち) ...
... (コードが保存されると、テストが実行される) ...
[info] Passed: : Total 9, Failed 0, Errors 0, Passed 9, Skipped 0
[success] Total time: 2 s, completed 2013/03/31 15:35:49
1. Waiting for source changes... (press enter to interrupt)
... (Enterを入力) ...
>

6 sbtの設定ファイル

簡単に設定を行う場合はbuild.sbtを、込み入った設定を行う場合はproject/build.scalaに設定を記述します。 サンプルではbuild.sbtを使用しています。

6.1 build.sbt

build.sbtはプロジェクトのディレクトリ直下に配置します。 設定の間には必ず空行が必要です。 設定例はサンプルコードのbuild.sbtを参照してください。コメントに説明を記載してあります。

7 REPL(Read Eval and Print Loop)

sbtのプロンプトでconsoleコマンドを実行すると、 プロジェクトで使用するライブラリやコードがクラスパスに追加された状態でREPLが起動します。3 ライブラリやコードの動きを確認する場合に便利です。

REPLで実行したコードは、ホームディレクトリの.scala_historyに保存されます。 REPLを終了する場合、:quitと入力するか、Ctrl+Dを押します。

7.1 REPLの特殊なコマンド

REPL上で実行可能な:で始まる特殊なコマンドがあります。

コマンド動作
:load src/main/scala/Web.scalascalaのコードを読み込む
:paste複数行入力するためのモードに入ります
:helpヘルプを見る

7.2 REPLのキーバインド

ScalaのREPLはEmacs風のキーバインドになっています。

キーストローク動作
Ctrl + f
Ctrl + b
Ctrl + p実行したコマンド履歴の一つ前に戻る
Ctrl + n実行したコマンド履歴の一つ先に進める
Ctrl + a行頭に移動
Ctrl + e行末に移動
Ctrl + rコマンド履歴からインクリメンタルサーチ
Meta + f1単語進む
Meta + b1単語戻る
Ctrl + kカーソル位置から行末まで削除
Ctrl + y*** 押しちゃ駄目だ! *** 4

8 ライブラリやサンプルコードをREPLから触ってみる

8.1 Webページを取得(dispatch + jsoup)

# 指定されたURLにアクセスして文字列として取得
scala> import dispatch.classic._
scala> val resp = Http(url("http://www.google.co.jp").as_str)
scala> println(resp)

# JSoupラッパーを使う
scala> import dispatch.classic.jsoup.JSoupHttp._
scala> val resp = Http(url("http://www.scala-lang.org/node/27499").as_jsouped)
scala> resp.select("div").size
scala> import scala.collection.JavaConverters._
scala> resp.select("#CommunityProjects ~ p ~ ul li strong").asScala.map(_.text)

# src/main/scala/Web.scalaのコードを呼ぶ
scala> val resp = Web.extract("http://www.scala-lang.org/node/27499", "#CommunityProjects ~ p ~ ul li strong")
scala> println(resp)

8.2 形態素解析(kuromoji)

scala> import org.atilika.kuromoji._
scala> import scala.collection.JavaConverters._
scala> val tokenizer = Tokenizer.builder().build()
scala> val result = tokenizer.tokenize("すもももももももものうち").asScala
scala> result map (t => (t.getSurfaceForm, t.getAllFeatures)) foreach println
scala> val token = result.head
scala> token.(ここでタブを打つと補完候補が表示される)

# src/main/scala/Analyzer.scalaのコードを呼ぶ
scala> val result = Analyzer.tokenize("記者が汽車で帰社した")
scala> result.filter(Analyzer.isNoun).distinct

8.3 HTMLを生成

src/script/Util.scalaにHTML確認用コードを用意しています。

sbtを2つ起動して一つでrunコマンドからサーバーを起動します。 もうひとつのsbtでconsoleからREPLを起動し、Util.writeメソッドでtemp.htmlに出力されます。 ブラウザでhttp://localhost:(port)/resources/temp.htmlを開くと出力したページを確認できます。

scala> :load src/script/Util.scala
Loading src/script/Util.scala...
defined module Util

scala> val result = Seq(Word("foo", 4), Word("bar", 1))
scala> Util.write(Template.frequentWordPage("hoge")(result))
(別のsbtでサーバを起動した状態で、ブラウザから/resources/temp.htmlを見ると生成したページが表示されます)

Footnotes:

1 ひとつのプロジェクトに複数のサブプロジェクトを含めることも可能です。

2 明示的な有効化が必要な機能や使用が推奨されていない機能を使用した場合に警告やエラーが発生します。 詳細を確認する場合はコンパイルオプションを設定して、警告の詳細が確認可能です。

> console
Welcome to Scala version 2.10.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
Type in expressions to have them evaluated.
Type :help for more information.
scala> 1 toString
warning: there were 1 feature warning(s); re-run with -feature for details
res0: String = 1
scala> :quit
> set scalacOptions += "-feature"
> console
Welcome to Scala version 2.10.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
Type in expressions to have them evaluated.
Type :help for more information.
scala> 1 toString
<console>:11: warning: postfix operator toString should be enabled
by making the implicit value language.postfixOps visible.
This can be achieved by adding the import clause 'import scala.language.postfixOps'
or by setting the compiler option -language:postfixOps.
See the Scala docs for value scala.language.postfixOps for a discussion
why the feature should be explicitly enabled.
              1 toString
                ^
res0: String = 1

3 consoleを実行すると、REPL起動前にコンパイルが実行されます。ここでコンパイルエラーになるとREPLが起動しません。 console-quickを使うと、コンパイルをせず、クラスパスの追加なしでREPLを起動します。 しかし、サンプルコードではREPL起動時にnascalaパッケージをインポートするようにsbtで設定しているため、 クラスパスを追加しないとエラーが発生し、REPLが起動できません。

4 Ctrl + yでヤンク(ペースト)すると思いきや、sbtがバックグランド実行になって、シェルに戻ってしまいます。 fgコマンドで復帰できますが、その後のショートカットキーの動作がおかしくなってしまいます。 (解決方法を知っている方がいたら、教えてください。)

Date: 2013-04-17 19:29:41 JST

Author: Yasuyuki Maeda(@maeda_)

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0