Scala入門記007 sbt + ScalaLogging
Javaの潤沢なライブラリが使えるScalaですが、そのまま使おうとしてもあまりイケてないことが多いです。
Play Frameworkのplay.api.Loggerを使っていると、通常はメッセージを渡す代わりに関数を渡せます。
そのためメッセージ文字列の生成を指定したログレベルが有効な時に限定できたりして実にScalaっぽいです。
今回はScalaLoggingというものがあるということなので使ってみました。
Scala入門記の目次はこちらです。
ScalaLogging
Scalaのマクロを使って、logger.isDebugEnabled等の判断をコンパイル時に追加しているらしいです。
PlayのLoggerはLoggerをラップして対応しているところをマクロで対応しているということですね。
sbtの設定
GitHubのドキュメントにはsbtの依存ライブラリに以下を追加するように書かれています。
libraryDependencies += "com.typesafe" %% "scalalogging-slf4j" % "1.0.1"
流し読みしてこれだけ設定すると、Loggerの実装がないという旨の以下のようにメッセージが出ます。
SLF4J: Defaulting to no-operation (NOP) logger implementation
ScalaLoggingはLoggerの実装としてSLF4J かLog4j2かを選択してsbtで設定しなければならないです。
SLF4J
SLF4Jを使うためにまずはこちらのサイトを参考に以下のように設定してみました。
libraryDependencies ++= Seq( "com.typesafe" %% "scalalogging-slf4j" % "1.0.1", "ch.qos.logback" % "logback-classic" % "1.0.13" )
この設定をしたうえで入門記006で使ったHelloオブジェクトを以下のように変更しました。
package example import com.typesafe.scalalogging.slf4j.Logging object Hello extends Logging { def main(args: Array[String]) { println(createGreeting(args)) } def createGreeting(args: Array[String]) = { logger.info("args size:" + args.length) args.headOption match { case Some(name) => "Hello " + name + "!" case None => "Hello!" } } }
これで"sbt run"するとログが表示されるようになりました。
Log4j2
まだbetaですがLog4jの2.0系のライブラリも使えます。
こちらはライブラリへの依存に加え、最低限動かすにも設定ファイル(log4j2.xml)が必要です。
libraryDependencies ++= Seq( "com.typesafe" %% "scalalogging-log4j" % "1.0.1", "org.apache.logging.log4j" % "log4j-core" % "2.0-beta9" )
また、src/main/resources/log4j2.xmlは以下のように設定しました。
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </appenders> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
ソースコードはpackageの指定のところだけがSLF4Jと異なります。
package example import com.typesafe.scalalogging.log4j.Logging object Hello extends Logging { def main(args: Array[String]) { println(createGreeting(args)) } def createGreeting(args: Array[String]) = { logger.info("args size:" + args.length) args.headOption match { case Some(name) => "Hello " + name + "!" case None => "Hello!" } } }
これで"sbt run"するSLF4Jと同様にログが表示されるようになりました。
依存ライブラリ
参考までに実際の依存ライブラリをInteliJ IDEAで表示してみました。
追加前
SLF4J
Log4j2
ソースコード
今回の記事で使ったソースコードは以下のGitリポジトリの2つのタグです。
https://github.com/thachi/scala-sample-001
- 007-log4j
- 007-slf4j
Scala入門記006 sbt + ScalaTest
プログラムを書いて何かしらのシステム開発するにはテストは必須だと思います。
プログラミング中に書くUnitTestの環境は現代の開発には欠かせないものでしょう。
今回はsbtによるScalaTtestとEclipse, IntelliJ IDEAでのテストスタイルについてまとめてみました。
Scala入門記の目次はこちらです。
テスト対象
テスト対象にするため、Scala入門記002 初めてのScalaプログラムで書いたHelloオブジェクトに挨拶文を作るためのメソッドを作りました。
1つ目の引数が存在する場合はその引数に応じて「Hello ほげほげ!」という挨拶文を生成します。
引数がない場合は「Hello!」という挨拶文を生成します。
object Hello { def main(args: Array[String]) { println(createGreeting(args)) } def createGreeting(args: Array[String]) = { args.headOption match { case Some(name) => "Hello " + name + "!" case None => "Hello!" } } }
これは、
sbt run
を実行すると
Hello!
を出力します。
また、引数を付けて
sbt "run Takashi"
を実行すると
Hello Takashi!
を出力します。
sbtの設定
ScalaTestを実行するにはScalaTestのライブラリに依存する必要があります。
build.sbt
に
libraryDependencies += "org.scalatest" % "scalatest_2.10" % "2.0" % "test"
を追記するとScalaTestがtest実行時のみ使えるようになります。
テストを書くときは
sbt ~test
とすると自動実行されて便利です。
テストコード
ScalaTestは色々なスタイルでテストコードを記述できるようです。
詳細はSelecting testing stylesを見ると紹介されています。
FlatSpec
まずはScalaSpecの公式サイトに習ってHelloSpecを作りました。
import org.scalatest._ class HelloSpec extends FlatSpec with Matchers { "Hello" should "create a simple greeting" in { val greeting = Hello.createGreeting(Array()) greeting should be("Hello!") } it should "create a greeting with name" in { val greeting = Hello.createGreeting(Array("Takashi")) greeting should be("Hello Takashi!") } }
FunSuite
import org.scalatest.FunSuite class HelloSuite extends FunSuite { test("引数を指定しない場合に\"Hello!\"が取得できる") { val actual = Hello.createGreeting(Array()) assert(actual == "Hello!") } test("引数に\"Takashi\"を指定した場合に\"Hello Takashi!\"が取得できる") { val actual = Hello.createGreeting(Array("Takashi")) assert(actual == "Hello Takashi!") } }
今回は日本語も使ってみました。
macのターミナルでは正常に日本語が表示できました。
JUnit
build.sbt
に
libraryDependencies += "junit" % "junit" % "4.11" % "test"
を追加するとJUnitの形式も使えます。
import org.junit.Test import org.scalatest.Assertions class HelloTest extends Assertions { @Test def createGreetingWithEmptyArgs { val greeting = Hello.createGreeting(Array()) assert(greeting == "Hello!") } @Test def createGreetingWithArgs { val greeting = Hello.createGreeting(Array("Takashi")) assert(greeting == "Hello Takashi!") } }
残念なことにこのJUnitテストは
sbt test
では無視されてしまいます。
私は2番めのFunSuiteが好み&日本語もありかな、と思いました。
今回の環境
Scala入門記005 sbt + IntelliJ IDEA CE
Scalaすごく良いです。caseクラスがステキです。
最近はやっとJavaで考えてScalaを書く時期は終わりました。
逆にJavaを書いている時に「Scalaだったら。。。」って考えてしまいます。
今回はIntelliJ IDEA CEでScalaを開発してみます。
Scala入門記の目次はこちらです。
IDEの準備
Eclipseからユーザが移りつつあると言われているJavaのIDEですがもちろんScalaも開発できます。
JetBrainsのサイトからダウンロードできます。
環境としては無料で使えるIntelliJ IDEA CE 13に以下の3つのプラグインを入れてみました。
sbtの設定
Eclipseのサイト同様にsbt-ideaというプラグインでsbtプロジェクトをIntelliJ IDEAに対応させることができます。
ドキュメントの通り
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.2")
をplugin.sbtに追加します。
私はEclipseの際と同様に
~/.sbt/0.13/plugins/plugins.sbt
で設定しました。
IntelliJ IDEAのプロジェクトにするにはsbtプロジェクトで
sbt "gen-idea"
とします。
これでIntelliJ IDEAの「Open Project」からそのまま開けます。
gen-ideaはデフォルトでソースやドキュメントがダウンロードされます。
不要な場合は
sbt "gen-idea no-classifiers"
とする必要があるようです。
Scala入門記004 sbt + Eclipse
現時点でのIDEの選択肢はEclipseかIntelliJ IDEAの2択のようです。
今回はsbtプロジェクトをEclipseに対応させていきます。
Scala入門記の目次はこちらです。
ScalaプロジェクトをEclipseで使う際はEclipse + Scala IDEという構成が今の最良の環境でしょう。
この記事を書いた時点での最新環境として、「Eclipseは4.3 SR1」 + 「Scala IDE 3.0.2」を用意しました。
公式サイトはScala IDE for Eclipseです。
Eclipse Marketplaceからインストールできました。
プロジェクトの設定
Eclipse対応のプロジェクトにするにはsbteclipseというプラグインを使う必要があります。
これはproject/plugins.sbtに以下の記述をすると使えるようになります。
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0")
これで、
sbt eclipse
とすると、Eclipseに対応した.projectと.classpathが生成されます。
これでEclipseから「Existing Projects into Workspace」でワークスペースにインポートすることができます。
また、
sbt "eclipse with-source=true"
を実行するとソースがダウンロードされます。
私はよく以下の様な間違いをします。
sbt eclipse with-source=true sbt "eclipse with-sources=true" sbt "eclipse -with-source=true"
これらはエラーになります。
mavenとごちゃごちゃになってます(^_^;)
グローバルな設定
このsbteclipseの設定ですが、複数の人で共同開発する際にはリポジトリにコミットしたくないかもしれません。
そんな場合は
~/.sbt/0.13/plugins/plugins.sbt
に
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0")
を書くと、どのsbtプロジェクトでもsbteclipseプラグインを使うことができるようになります。
私はグローバルに設定することにしました。
Scala入門記003 初めてのsbt
実は最近Scala結構やってます。
個人的にもScalaを積極的に使っていくので疑問点を潰していく上でも入門記を続けようと思います。
Scala入門記の目次はこちらです。
前回も紹介しましたが、Scalaでビルドする際はsbtが標準的なツールです。
これをmavenと同等以上に使えるようになりたいところです。
まずは使いはじめるためには公式ドキュメントの和訳「始める sbt - ようこそ」を適宜読んでいくのが良さそうです。
準備
今日は軽く最初から試してみました。
まずは適当なフォルダにビルドの定義build.sbtを作ります。
name := "scala-ample-001" version := "1.0" scalaVersion := "2.10.3"
プロジェクト名とそのバージョン、使うscalaのバージョンを指定しただけです。
そしてソースコードを用意します。
ファイルはsrc/main/scala/Hello.scalaにしました。
object Hello { def main(args: Array[String]) { println("Hello!") } }
実行
実行は前回と同じで以下のコマンドで「Hello!」が表示されます。
sbt run
パッケージング
jarを作るにはpackageです。
sbt package
これで./target/scala-2.10/scala-ample-001_2.10-1.0.jarができます。
scala ./target/scala-2.10/scala-ample-001_2.10-1.0.jar
で生成したjarのHello#mainが実行されます。
ちゃんとjarファイルにはマニフェストファイルが入っています。
sbt clean
でtargetフォルダがなくなります。
project/targetはなくならないようです。
実験
scalaVersion := "2.10.3"
を
scalaVersion := "2.10.2"
に変えてもrunできました。
scalaVersion := "2.10"
に変えるとエラーになりました。
scalaVersion := "2.10.5"
に変えるてもエラーになりました。
ログを見ていると、コンパイラをダウンロードしてコンパイルしてくれてるようでした。
なので端末に入っていないscalaのバージョンを指定してもビルドできるので便利そうです。
ちなみに2.10.5はリリースされていないのでエラーになったのですね。
最後にscalaVersionを消してしまった場合もコンパイルでき、この際はおそらくシステムのデフォルトが使われるのでしょう。
git
今回のソースをgit管理をする際は.gitignoreは以下としました。
target
今回のサンプルからGitHub管理していきます。
https://github.com/thachi/scala-sample-001
CocoaPods
私が普段使っているXcodeプロジェクトの依存ライブラリの管理ツールの紹介です。
まず、サイトはこちら→ http://cocoapods.org/
簡単なのでこれを見ればいいけれどザクッと紹介します。
Podfileを見つけた場合
Podfileがあるプロジェクトを見たら、xcodeprojは開かずに
pod install
するとxxxx.xcworkspaceができるのでそれを開きます。
podなんて使えない場合
もしpodが入っていなければCocoaPodsを入れる。
※gem(=RubyGems)はmacに最初から入っていた気がします
sudo gem install cocoapods
まとめ
これだけでXcodeのライブラリ管理は幸せになることまちがいなしです。