torsdag 24 februari 2011

Building a DSL with groovy

I am currently working an a small 2-d game, where there're supposed to be some organisms spread out over the "world". I soon realized that I will have to test different setups of the world with organisms spread out.
I wrote some tests for different locations of organisms, and added some comments to explain it visually.

The original test contained something like this:

/**
* This creates a world with two organisms, located:
* ---------------
* |______________|
* |_____1________|
* |__________2___|
* |______________|
*
*/
def world = new World(organisms: [new Organism(id:1, x:10,y:10), new Organism(id:2, x:20, y:20)])

This was an obvious candidate for a DSL; I beleive that any time you feel the need to explain your code (visually) with a comment, you should consider building a DSL instead.


This is my first draft:

class Organism {
def id, x, y, radius
}
class World {
def width, height
def organisms = []
}
class WorldBuilder {
def yIndex = 0
def organisms = []
def radius = 1
def xIndex = 0
def getProperty(String name) {
def metaProperty = metaClass.getMetaProperty(name)
if (!metaProperty) {
if(name.length() > xIndex){
xIndex = name.length()
}
name.eachWithIndex {character, index ->
if (character != '_') {
println "create an organism with: x:$index, y:${yIndex}, radius: 1"
organisms << new Organism(id:character, x:index, y:yIndex, radius: radius )
}
}
yIndex++
println "get the property with name: $name"
return this
}else{
return metaProperty.getProperty(this)
}
}

def build() {
new World(organisms: organisms, width: xIndex, height: yIndex)
}
}

class WorldBuilderTests extends GroovyTestCase {
void test_build() {
World world = new WorldBuilder().with {
_________1_____2
__3__4_____a___6
________________
}.build()
assertEquals(6, world.organisms.size())

def org1 = world?.organisms.find { it.id == "1" }
assertEquals(1, org1.radius)
assertEquals(9, org1.x)
assertEquals(0, org1.y)

assertEquals(16, world.width)
assertEquals(3, world.height)

}
}

onsdag 9 februari 2011

GroovyWS: setting endpoint and soap headers

I've seen questions on various forums regarding how to change the endpoint and add soap headers with GroovyWS.
Since GroovyWS is using CXF under its cover it is possible (albeit not intuitive) to do:


@Grab(
group='org.codehaus.groovy.modules',
module='groovyws',
version='0.5.2')
import groovyx.net.ws.WSClient
import org.apache.cxf.headers.Header
import javax.xml.soap.SOAPFactory
import javax.xml.namespace.QName
import org.apache.cxf.binding.soap.SoapHeader
import javax.xml.ws.BindingProvider
import org.apache.cxf.interceptor.LoggingOutInterceptor

proxy = new WSClient(
"http://www.w3schools.com/webservices/tempconvert.asmx?WSDL", this.class.classLoader)
proxy.initialize()

def cxfClient = proxy.client

def headers = []

// Add header
SOAPFactory sf = SOAPFactory.newInstance()
def authElement = sf.createElement(
new QName("a_namespace", "a_name"))

def productToken =
authElement.addChildElement("productToken")

def tokenHeader = new SoapHeader(
new QName("a_namespace", "a_name"), authElement);
headers.add(tokenHeader);

cxfClient.getRequestContext()
.put(Header.HEADER_LIST, headers)

// Set endpoint
cxfClient.getRequestContext()
.put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "another_url")

// Add a logging interceptor
cxfClient.outInterceptors.add(
new LoggingOutInterceptor())

result = proxy.CelsiusToFahrenheit(0)
println """You are probably freezing at
${result} degrees Farhenheit"""