Saturday, April 9, 2011

Gradle, Groovy, and MethodMissing

I've created a few of these, but I think my latest is the cleanest implementation I've done yet. I've created a new ProjectTemplate builder for laying out new projects from scratch. Why, you ask? Because I seem to be always creating new projects and importing them into my IDE. Or, I use the IDE to create them and then wind up with a bunch of IDE files stuck in source control. Personally, I prefer the former.

Normally creating the initial project structure isn't that big of a deal, it's just a few simple *nix commands really:
mkdir -p MyProject/src/
cd MyProject/src
mkdir -p main/java main/resources test/java test/resources

I could just create a small bash script to handle this for me, but I'd have to change it around all the time to handle the project name, or if I wanted to use Groovy instead of Java, etc... I could also just make the bash script handle user input, but I'd still want to set the project up with some sort of build system, and it'd be cool if the build system could do this for me.

As I've been a fan of Gradle for a while now, I wanted to see if I could do something to get Gradle to do this for me. I know Maven can do this already with archetypes, but as I said, I wanted Gradle to be able to do this for me.

Without getting into the Gradle bit too much just yet, because I want to do that properly, and it's a whole other ball of wax, I'll focus on the Groovy bits first.

So, the goal is to have a small-ish Gradle build script that'll let me create a directory structure for a new Groovy project. A pretty standard directory structure looks like this:
[project root]/
   src/
      main/
         groovy/
         resources/
      test/
         groovy/
         resources/
Let's start with a basic Gradle build script: build.gradle
task "create-groovy-project" << {
   String projectName = System.console().readLine("> Project Name: ")
   println "Creating directory structure for new Groovy project: ${projectName}"
}

Running 'gradle create-groovy-project' now results in us being asked for our new project's name, and then it being echoed back at us.

Now for a little Groovy fun. Here's a shortened version of my ProjectTemplate class. This should just be pasted into the top of your build.gradle file.
class ProjectTemplate {
   
   private File parent
   
   private ProjectTemplate() {}
   
   static void root(String path, Closure closure) {
      new ProjectTemplate().d(path, closure)
   }

   void d(String name, Closure closure = {}) {
      File oldParent = parent
      if (parent) {
         parent = new File(parent, name)
      } else {
         parent = new File(name)
      }
      parent.mkdirs()
      closure.delegate = this
      closure()
      parent = oldParent
   }
   void f(Map args = [:], String name) {
      File file
      if (parent) {
         file = new File(parent, name)
      } else {
         file = new File(name)
      }
      file.exists() ?: file.createNewFile()
      if (args.content) {
         def content = args.content.stripIndent()
         if (args.append) {
            file.append(content)
         } else {
            file.text = content
         }
      }
   }
}
Nothing too spiffy about it. You use it by calling the static 'root' method and then declaring your directories and files. Let's add it to our 'create-groovy-project' task:
task "create-groovy-project" << {
   String projectName = System.console().readLine("> Project Name: ")
   println "Creating directory structure for new Groovy project: ${projectName}"
   ProjectTemplate.root(projectName) {
      d("src") {
         d("main") {
            d("groovy")
            d("resources")
         }
         d("test") {
            d("groovy")
            d("resources")
         }
      }
      f("LICENSE.txt", content: "// LICENSE GOES HERE")
      f("README.txt", content: "// What is this project all about, and what's needed to build it?")
   }
}
Running 'gradle create-groovy-project' now, and giving the name "MyProject" results in a new directory called "MyProject" which contains a directory structure like:
MyProject/
   src/
      main/
         groovy/
         resources/
      test/
         groovy/
         resources/
   LICENSE.txt
   README.txt
Ok, so far, so good. We can now create basic directory structures for a new Groovy project, and it wouldn't be too hard at all to create an 'create-java-project', or 'create-scala-project' task now to create directory structures for those types of projects.

But the ProjectTemplate usage is still a little noisy for me. We can get rid of the parenthesis, but it's still not enough.

Let's add a methodMissing method to the ProjectTemplate class to make things cleaner:
def methodMissing(String name, def args) {
      if (args) {
         def arg = args[0]
         if (arg instanceof Closure) {
            d(name, arg)
         } else if (arg instanceof Map) {
            f(arg, name)
         } else if (arg instanceof String || arg instanceof GString) {
            f([content: arg], name)
         } else {
            println "Couldn't figure out what to do. name: ${name}, arg: ${arg}, type: ${arg.getClass()}"
         }
      }
   }
What does this buy us? Well, now we don't need to specify the method name anymore. That's right kiddies, no more d's or f's. :)

We can change our usage to this now:
ProjectTemplate.root(projectName) {
      "src" {
         "main" {
            "groovy" {}
            "resources" {}
         }
         "test" {
            "groovy" {}
            "resources" {}
         }
      }
      "LICENSE.txt" "// LICENSE GOES HERE"
      "README.txt" "// What is this project all about, and what's needed to build it?"
   }
This is a lot cleaner, and easier to read to me.

I am actually working on a Gradle plugin which I hope to make available very soon, but for now It's hosted at LaunchPad. 

You can download the source using Bazaar:
bzr branch lp:gradle-templates
I've uploaded the complete build.gradle file on TellurianRing.com (1).

I'll blog more about my Gradle plugin endeavors as they happen.

Cheers,
Eric

(1) build.gradle (right click and save as)

Monday, March 7, 2011

More jEdit mode-ulation

A small group of my fellow developers at eHarmony Inc. have been trying out ATDD (Acceptance Test Driven Development) with quite a bit of success, I hear. They've been using Spock to create their tests.

I too have been trying out ATDD, more specifically BDD (Behavior Driven Development) while developing TeamMochi. I found myself writing out behavioral tests in plain text first before moving them over to something like Spock. Again, I found myself doing so in my favorite text editor jEdit, and given my recent trials with jEdit's mode files, I created another mode file to help myself with creating these tests.

The mode file supports the main keywords like, "given", "when", "then", and some extra ones like: "where", "if", "and", "but", and "should". It also supports variable placeholders, and some simple markup for variable data.

All this boils down to a simple syntax highlighted user story using the normal BDD language.

Eg. A basic story like:
Given that the User is on the website
and the User is registered with the email 'kyle@example.com' and password 'password'
When the user attempts to login with his registered email and password
Then the User should be logged in and taken to his dashboard.
Looks like this in jEdit:


You'll notice that I borrowed the entity recognition from my 'domain' mode. Any word that starts with a capital appears as a label in jEdit.

This might be something given to you by the Product Owner, or preferably something you and the Product Owner came up with together.

But the developer might want to take it a step further and supply data which can be used in the test.

This is where variable placeholders, and the simple markup comes in to play.

We might for instance replace the actual email address and password in the previous example with variables, then create a "where" clause with a table of valid email addresses, and passwords for test accounts.

The text would look something like this:
Given that the User is on the website
and the User is registered with a valid email and password
When the user attempts to login with his registered ${email} and ${password}
Then the User should be logged in and taken to his dashboard.
Where (
    |= email             =|= password =|
    | 'kyle@example.com'  | 'password' |
    | 'kenny@example.com' | 'password' |
)
jEdit would show:

It also supports key, value pair markup like so:

I don't have any plans of including this mode file in jEdit core, but I've uploaded it to the daily-build server (1).

Enjoy :)

(1) behavior-test.xml (right click and save as)

Tuesday, March 1, 2011

jEdit mode-ulation

I've been playing around with jEdit mode files again. Came up with one I've committed to jEdit core, and another one I've been using with my work on TeamMochi. Both work really well with folding set to 'indent', and wrap style set to 'soft'.

The first one is a simple outline mode file which sets up syntax highlighting.

Text from WikiPedia
 As I mentioned, this mode file has been committed to jEdit core, and is available in the jEdit daily builds (1). I've also made the individual mode file available as well (2).

The second mode file is one for "domain" type text. Again, just simple syntax highlighting for what I consider a simple domain specific language (DSL).

This one is not available in jEdit core, but I've uploaded it to the daily-build server also (3).

Cheers.
Eric

(1) jEdit Daily Build (trunk)
(2) outline.xml (right click, and save as)
(3) domain.xml (right click, and save as)