Saturday July 24, 2010

With the release of version 2.8 and enthusiasm amongst my current project's collaborators, I've fired up my latent interest in scala. There's a lot to like about scala: function and object orientation, message based concurrency and JVM garbage collection, JITing, etc; it's a really interesting language. The initial creature comforts I've been looking for are a development, build and test environment I can be productive in. At present, it looks like the best tools for me to get rolling are IntelliJ, sbt and ScalaTest. In this post, I'll recount the setup I've arrived at on a MacBook Pro (Snow Leopard), so far so good.

Development:

I've used Eclipse for years and there's a lot I like about it but I've also had stability problems, particularly as plugins are added/removed from the installation (stay away from the Aptana ruby plugin, every attempt to use it results in an unstable Eclipse for me). So I got a new version of IntelliJ ("Community Edition"); the Scala plugin for v2.8 doesn't work with the IntelliJ v9.0.2 release so the next task was to grab and install a pre-release of v9.0.3 (see ideaIC-95.413.dmg). I downloaded recent build of the scala plugin (see scala-intellij-bin-0.3.1866.zip) and unzipped it in IntelliJ's plugins directory (in my case, "/Applications/IntelliJ IDEA 9.0.3 CE.app/plugins/"). I launched IntelliJ, Scala v2.8 support was enabled.

Build:

I've had love/hate relationships in the past with ant and maven; there's a lot to love for flexibility with the former and consistency with the latter (hat tip at "convention over configuration"). There's a lot to hate with the tedious XML maintenance that comes with both. So this go around, I'm kicking the tires on simple build tool. One of the really nice bits about sbt is that it has a shell you can run build commands in; instead of waiting for the (slow) JVM launch time everytime you build/test you can incur the launch penalty once to launch the shell and re-run those tasks from within the sbt shell. Once sbt is setup, a project can be started from the console like this: $ sbt update

You'll go into a dialog like this:

Project does not exist, create new project? (y/N/s) y Name: scratch Organization: ohai Version [1.0]: 0.1 Scala version [2.7.7]: 2.8.0 sbt version [0.7.4]:

mkdir project/build

vi project/build/Project.scala

import sbt._ class Project(info: ProjectInfo) extends DefaultProject(info) with IdeaProject { // v1.2 is the current version compatible with scala 2.8 // see http://www.scalatest.org/download val scalatest = "org.scalatest" % "scalatest" % "1.2" % "test->default" }

mkdir -p project/plugins/src

vi project/plugins/src/Plugins.scala

import sbt._ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" }

sbt update

sbt idea

Now I want sbt to not only bootstrap the build system but also bootstrap the IntelliJ project. There's an sbt plugin for that. Ironically, bringing up the develop project requires creating folder structures and code artifacts... which is what your development environment should be helping you with. While we're in there, we'll declare our dependency on the test framework we want (why ScalaTest isn't in the scala stdlib is mysterious to me; c'mon scala, it's 2010, python and ruby have both shipped with test support for years). So, fallback to console commands and vi (I don't do emacs) or lean on Textmate.Back in IntelliJ, do "File" > "New Module" > select "Import existing module" and specify the path to the "scratch.iml" file that that last console command produced.

Note how we declared our dependency on the test library and the repository to get it from with two lines of code, not the several lines of XML that would be used for each in maven.

Test:

Back in IntelliJ, right click on src/main/scala and select "New" > "Package" and specify "ohai.scratch". Right click on that package and select "New" > "Scala Class", we'll create a class ohai.scratch.Bicycle - in the editor put something like

package ohai.scratch class Bicycle { var turns = 0 def pedal { turns +=1 } }

package ohai.scratch.test import org.scalatest.Spec import ohai.scratch.Bicycle class BicycleSpec extends Spec { describe("A bicycle, prior to any pedaling -") { val bike = new Bicycle() it("pedal should not have turned") { expect(0) { bike.turns } } } describe("A bicycle") { val bike = new Bicycle() bike.pedal it("after pedaling once, it should have one turn") { expect(1) { bike.turns } } } }

Now to test our bicycle, do similar package and class creation steps in the src/test/scala folder to create ohai.scratch.test.BicycleTest:Back at your console, go into the sbt shell by typing "sbt" again. In the sbt shell, type "test". In the editor, make your bicycle do more things, write tests for those things (or reverse the order, TDD style) and type "test" again in the sbt shell. Lather, rinse, repeat, have fun.