Groovy DSL fun again!


So I’m writing these ad-hoc Groovy scripts to update a chart on an internal wiki page when I start feeling the urge to play with the syntax. Groovy is so much fun to use that you just can’t help but mangle with things once you get the hang of it. Hi, I’m Cliff. You’re here because you like to alter the rules of your programming language while it’s running.

It’s been some years since I fooled with GSpec and all the meta-programming stuff. So now I start my latest script (half into the project) and decide to express what I want the script to work like before I write the code. It’s a fairly straight forward task and I find it very easy to bend Groovy syntax to say exactly what I’m thinking while remaining executable. So I start with this:

def whenWeConvertItToTableMarkup(givenJson, andGivenTableMarkup, andGivenApkVersionString){
    return "I'm not converted yet! :("
}

def then(action) { action() }

def givenJson = '''
{"my-latest.apk":
    {"totalSizeFormatted":"14.84 MB","imagesSizeFormatted":"6.22 MB",
    "textSizeFormatted":"426.37 KB",
    "resourcesSizeFormatted":"2.04 MB","executableSizeFormatted":"1.15 MB","audioFilesSizeFormatted":"376.10 KB",
    "totalSize":15562499,"imagesSize":6517513,"textSize":436603,
    "resourcesSize":2141448,"executableSize":1202497,"audioFilesSize":385126},
}'''

def andGivenTableMarkup = '''\t| Version | 2.6.0.95 | 2.7.0.907 | 2.7.0.909 | 2.7.0.911 | 2.8.0.20 | 2.8.0.16 |
\t| Executable | 574.13 | 598 | 598.94 | 598.54 | 594 | 592.80 |
\t| Resources | 1580 | 1720 | 1720 | 1720 | 1800 | 1800 |
\t| Images | 3980 | 5240 | 5240 | 5240 | 6170 | 6160 |
\t| Text | 264.35 | 278.59 | 278.57 | 278.59 | 293.30 | 293.93 |
\t| Audio Files | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 |
\t| Total | 11550 | 13110 | 13080 | 13110 | 14160 | 14370 |
'''

def andGivenApkVersionString = '2.8.0.25'

def result = whenWeConvertItToTableMarkup(givenJson, andGivenTableMarkup, andGivenApkVersionString)
then {
    assert result == '''\t| Version | 2.6.0.95 | 2.7.0.907 | 2.7.0.909 | 2.7.0.911 | 2.8.0.20 | 2.8.0.16 | 2.8.0.25 |
\t| Executable | 574.13 | 598 | 598.94 | 598.54 | 594 | 592.80 | 1174.31 |
\t| Resources | 1580 | 1720 | 1720 | 1720 | 1800 | 1800 | 2091.26 |
\t| Images | 3980 | 5240 | 5240 | 5240 | 6170 | 6160 | 6364.76 |
\t| Text | 264.35 | 278.59 | 278.57 | 278.59 | 293.30 | 293.93 | 426.37 |
\t| Audio Files | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 | 46.09 |
\t| Total | 11550 | 13110 | 13080 | 13110 | 14160 | 14370 | 15197.75 |
'''
}

Then taken some learning from my recent Python training I thought, “Hey, why not include this test as an expectations string at the bottom of my script, similar to how Python uses doc-strings!”

def expectations = """
def whenWeConvertItToTableMarkup(givenJson, andGivenTableMarkup, andGivenApkVersionString){
    return "I'm not converted yet! :("
}

def then(action) { action() }

def givenJson = '''
{"my-latest.apk":
    {"totalSizeFormatted":"14.84 MB","imagesSizeFormatted":"6.22 MB",
    "textSizeFormatted":"426.37 KB",
    "resourcesSizeFormatted":"2.04 MB","executableSizeFormatted":"1.15 MB","audioFilesSizeFormatted":"376.10 KB",
    "totalSize":15562499,"imagesSize":6517513,"textSize":436603,
    "resourcesSize":2141448,"executableSize":1202497,"audioFilesSize":385126},
}'''

def andGivenTableMarkup = '''\t| Version | 2.6.0.95 | 2.7.0.907 | 2.7.0.909 | 2.7.0.911 | 2.8.0.20 | 2.8.0.16 |
\t| Executable | 574.13 | 598 | 598.94 | 598.54 | 594 | 592.80 |
\t| Resources | 1580 | 1720 | 1720 | 1720 | 1800 | 1800 |
\t| Images | 3980 | 5240 | 5240 | 5240 | 6170 | 6160 |
\t| Text | 264.35 | 278.59 | 278.57 | 278.59 | 293.30 | 293.93 |
\t| Audio Files | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 |
\t| Total | 11550 | 13110 | 13080 | 13110 | 14160 | 14370 |
'''

def andGivenApkVersionString = '2.8.0.25'

def result = whenWeConvertItToTableMarkup(givenJson, andGivenTableMarkup, andGivenApkVersionString)
then {
    assert result == '''\t| Version | 2.6.0.95 | 2.7.0.907 | 2.7.0.909 | 2.7.0.911 | 2.8.0.20 | 2.8.0.16 | 2.8.0.25 |
\t| Executable | 574.13 | 598 | 598.94 | 598.54 | 594 | 592.80 | 1174.31 |
\t| Resources | 1580 | 1720 | 1720 | 1720 | 1800 | 1800 | 2091.26 |
\t| Images | 3980 | 5240 | 5240 | 5240 | 6170 | 6160 | 6364.76 |
\t| Text | 264.35 | 278.59 | 278.57 | 278.59 | 293.30 | 293.93 | 426.37 |
\t| Audio Files | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 | 376.10 | 46.09 |
\t| Total | 11550 | 13110 | 13080 | 13110 | 14160 | 14370 | 15197.75 |
'''
}
"""

println expectations

Taking it a step further I can pass the expectation string to an eval call (or whatever the Groovy “eval” equivalent is, I’ve been mixing my scripting languages a lot these days…) and execute the test while passing the current object into the evaluated context. I can do all of this without frameworks or anything fancy. If I sprinkled just a little meta-programming into the mix then I could remove the definitions for the “then” and the “whenWeConvertItToTableMarkup” methods and have it read like a real spec!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 251 other followers

%d bloggers like this: