How to reuse code block which describe similiar ant build logic in groovy? - groovy

How to reuse code block which describe similiar ant build logic in groovy?
If we have build logic which was implemented by Groovy AntBuilder, just like code below:
ant.someTask(attr1:value1, attr2:value2) {
configuration1(param1:args1, param2:args2){
similiarStructure(additionalArgs:aaa){
setting1(param5:value5) {
//...blah blah blah
}
//further more settings, may be or may be not the same with similiarStructure below
}
}
configuration2(param3:args3, param4:args4){
similiarStructure(additionalArgs:aaa){
setting1(param5:value5) {
//...blah blah blah
}
//further more settings, may be or may be not the same with similiarStructure below
}
}
}
Are there any ways to reuse Groovy AntBuilder code block, which could brief the statment in configuration2 ?
I've try to predefine closures and inject them in both configuration,
but it fails with property not found exception while initializing closure.

I'll provide two answers so you can select which one is more appropriate for your use case and test it. The solutions depend on at what level you want the shared config.
If you want a more general purpose solution that allows you to share the whole of the similarStructure block, you need to perform some more advanced work. The trick is to ensure that the delegate of the shared configuration closure is set appropriately:
def sharedConfig = {
similarStructure(additionalArgs:aaa) {
setting1(param5:value5) {
//...blah blah blah
}
}
}
ant.someTask(attr1: value1, attr2: value2) {
configuration1(param1:args1, param2:args2){
applySharedConfig(delegate, sharedConfig)
}
configuration2(param3:args3, param4:args4){
applySharedConfig(delegate, sharedConfig)
}
}
void applySharedConfig(builder, config) {
def c = config.clone()
c.resolveStrategy = Closure.DELEGATE_FIRST
c.delegate = builder
c.call()
}
Although the applySharedConfig() method seems ugly, it can be used to share multiple configurations across different tasks.
One thing to bear in mind with this solution is that the resolveStrategy of the closure can be very important. I think both DELEGATE_FIRST and OWNER_FIRST (the default) will work fine here. If you run into what appear to be name resolution problems (missing methods or properties) you should try switching the resolution strategy.

I'll provide two answers so you can select which one is more appropriate for your use case and test it. The solutions depend on at what level you want the shared config.
If you are happy to simply share the closure that goes with similarStructure, then the solution is straightforward:
def sharedConfig = {
setting1(param5:value5) {
//...blah blah blah
}
}
ant.someTask(attr1: value1, attr2: value2) {
configuration1(param1:args1, param2:args2) {
similarStructure(additionalArgs:aaa, sharedConfig)
}
configuration2(param3:args3, param4:args4) {
similarStructure(additionalArgs:aaa, sharedConfig)
}
}
The method that is similarStructure should ensure that the sharedConfig closure is properly configured. I haven't tested this, so I'm not entirely sure. The disadvantage of this approach is that you have to duplicate the similarStructure call with its arguments.

Related

What is the use of a static initialization block in Groovy?

What is the use of the static initialization block in Groovy. Why does Geb use it? If the use of it is the same as in Java, then how can you initialize non-declared fields in a situtation like this?
class ManualsMenuModule extends Module {
static content = {
toggle { $("div.menu a.manuals") }
linksContainer { $("#manuals-menu") }
links { linksContainer.find("a") }
}
void open() {
toggle.click()
waitFor { !linksContainer.hasClass("animating") }
}
}
Some answers to your questions are provided in the section about the content DSL of the Geb manual.
The DSL is implemented using Groovy's methodMissing() mechanism and modification of the delegate of the closure assigned to the static content field. If you are interested in digging deeper then you can always look at the implementation in PageContentTemplateBuilder.
I'm not expert on Geb, I can only explain the groovy meaning of the code.
1st off, it's not the static initialization block like in java. In those lines static content = {...} you are assigning to a static variable a Closure instance which is evaluated and executed LATER (hence, lazy).
The closure represents a (part of) a Geb's Groovy Builder which is called by Geb framework to register/perform some tasks.
There's no Java counterpart to achieve the same, and that's the reason why groovy-based frameworks are so nice to use for testing purposes and they follow the general rule of thumb:
test code should be more abstract then the code under test
UPDATE:
This line:
toggle { $("div.menu a.manuals") }
can be rewritten like
toggle( { $("div.menu a.manuals") } )
or
def a = { $("div.menu a.manuals") }
toggle a
so it's a method invocation and not an assignment. In groovy you can omit the brackets in some cases.

Workaround for lack of generators/yield keyword in Groovy

Wondering if there is a way I can use sql.eachRow like a generator, to use it in a DSL context where a Collection or Iterator is expected. The use case I'm trying to go for is streaming JSON generation - what I'm trying to do is something like:
def generator = { sql.eachRow { yield it } }
jsonBuilder.root {
status "OK"
rows generator()
}
You would need continuation support (or similiar) for this to work to some extend. Groovy does not have continuations, the JVM also not. Normally continuation passing style works, but then the method eachRow would have to support that, which it of course does not. So the only way I see is a makeshift solution using threads or something like that. So maybe something like that would work for you:
def sync = new java.util.concurrent.SynchronousQueue()
Thread.start { sql.eachRow { sync.put(it) } }
jsonBuilder.root {
status "OK"
rows sync.take()
}
I am not stating, that this is a good solution, just a random consumer-producer-work-around for your problem.

The Cucumber JVM's (Groovy) "World (Hooks)" object mixin and Intellij navigation

I am developing cucumber scenarios using Cucumber JVM (Groovy) in intellij. Things are much better than doing the same in eclipse I must say.
I'd like to resolve one small problem to make things even better for my team. But since I am new to Intellij, I need help with the following please:
When I am in a step def file (groovy), Intellij can't seem to see variables and methods defined in the cucumber "World" object. So don't get IDE support (auto-complete, etc) for those which is a bit annoying. How can I fix that?
IntelliJ IDEA expects an object inside World closure. So, you could define the folloing block in EACH step definitions file:
World {
new MockedTestWorld()
}
MockedTestWorld.groovy:
class MockedTestWorld implements TestWorld {
#Lazy
#Delegate
#SuppressWarnings("GroovyAssignabilityCheck")
private TestWorld delegate = {
throw new UnsupportedOperationException("" +
"Test world mock is used ONLY for syntax highlighting in IDE" +
" and must be overridden by concrete 'InitSteps.groovy' implementation.")
}()
}
To cleanup duplicated world definitions we use last-glue initializers and a bit of copy-paste:
real/InitSteps.groovy
def world
GroovyBackend.instance.#worldClosures.clear()
GroovyBackend.instance.registerWorld {
return world ?: (world = new RealTestWorld1()) // Actual initialization is much longer
}
legacy/InitSteps.groovy
def world
GroovyBackend.instance.#worldClosures.clear()
GroovyBackend.instance.registerWorld {
return world ?: (world = new LegacyTestWorld1())
}
Finally, run configurations would be like this (with different glues):
// Real setup
glue = { "classpath:test/steps", "classpath:test/real", },
// Legacy setup
glue = { "classpath:test/steps", "classpath:test/legacy", },

Use 'owner' property in Groovy DSL

Let's consider a simple Groovy DSL
execute {
sendNotification owner
sendNotification payee
}
The implementation of execute is
public static void execute(Closure dslCode) {
Closure clonedCode = dslCode.clone()
def dslDelegate = new MyDslDelegate(owner: 'IncCorp', payee: 'TheBoss')
clonedCode.delegate = dslDelegate
clonedCode.call()
}
and custom Delegate is
public static class MyDslDelegate {
def owner
def payee
void sendNotification(to) {
println "Notification sent to $to"
}
}
The expected result of running execute block is
Notification sent to IncCorp
Notification sent to TheBoss
the actual one is
Notification sent to class package.OwnerClassName
Notification sent to TheBoss
The problem is owner is a reserved property in the Groovy Closure itself and no resolveStrategy options help to replace owner value with custom value from delegate due to Groovy getProperty implementation for Closure
public Object getProperty(final String property) {
if ("delegate".equals(property)) {
return getDelegate();
} else if ("owner".equals(property)) {
return getOwner();
...
} else {
switch(resolveStrategy) {
case DELEGATE_FIRST:
...
}
My question is how some one can outcome this limitation and use owner property name in a custom DSL?
This is a bit of a hack, but this should get you what you want, without altering Groovy source:
public static void execute(Closure dslCode) {
Closure clonedCode = dslCode.clone()
def dslDelegate = new MyDslDelegate(owner: 'IncCorp', payee: 'TheBoss')
clonedCode.#owner = dslDelegate.owner
clonedCode.resolveStrategy = Closure.DELEGATE_ONLY
clonedCode.delegate = dslDelegate
clonedCode.call()
}
Ref: Is it possible to change the owner of a closure?
The simple answer is no, you can't. 'owner' is a reserved keyword in Groovy, and therefore by definition cannot be used as an arbitrary symbol. Even if there is a way to hack around this, you're far better off just using a name that doesn't conflict with the implementation of the language- this is especially true in Groovy, which keeps promising to redesign its MOP completely, meaning that any hack you implement may well stop working in future versions.
Perhaps the question would make more sense if you explained why you are willing to offer a bounty and search for a way of hacking around this problem, rather than just changing the name to something different and avoiding the problem entirely. Reserved symbols are a pretty fundamental limitation of a language, and ever attempting to work around them seems very unwise.

Best groovy closure idiom replacing java inner classes?

As new to groovy...
I'm trying to replace the java idiom for event listeners, filters, etc.
My working code in groovy is the following:
def find() {
ODB odb = ODBFactory.open(files.nodupes); // data nucleus object database
Objects<Prospect> src = odb.getObjects(new QProspect());
src.each { println it };
odb.close();
}
class QProspect extends SimpleNativeQuery {
public boolean match(Prospect p) {
if (p.url) {
return p.url.endsWith(".biz");
}
return false;
}
}
Now, this is far from what I'm used to in java, where the implementation of the Query interface is done right inside the odb.getObjects() method. If I where to code "java" I'd probably do something like the following, yet it's not working:
Objects<Prospect> src = odb.getObjects( {
boolean match(p) {
if (p.url) {
return p.url.endsWith(".biz");
}
return false;
}
} as SimpleNativeQuery);
Or better, I'd like it to be like this:
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
However, what groovy does it to associate the "match" method with the outer script context and fail me.
I find groovy... groovy anyways so I'll stick to learning more about it. Thanks.
What I should've asked was how do we do the "anonymous" class in groovy. Here's the java idiom:
void defReadAFile() {
File[] files = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.getPath().endsWith(".biz");
}
});
}
Can groovy be as concise with no additional class declaration?
I think it would have helped you to get answers if you'd abstracted the problem so that it didn't rely on the Neodatis DB interface -- that threw me for a loop, as I've never used it. What I've written below about it is based on a very cursory analysis.
For that matter, I've never used Groovy either, though I like what I've seen of it. But seeing as no one else has answered yet, you're stuck with me :-)
I think the problem (or at least part of it) may be that you're expecting too much of the SimpleNativeQuery class from Neodatis. It doesn't look like it even tries to filter the objects before it adds them to the returned collection. I think instead you want to use org.neodatis.odb.impl.core.query.criteria.CriteriaQuery. (Note the "impl" in the package path. This has me a bit nervous, as I don't know for sure if this class is meant to be used by callers. But I don't see any other classes in Neodatis that allow for query criteria to be specified.)
But instead of using CriteriaQuery directly, I think you'd rather wrap it inside of a Groovy class so that you can use it with closures. So, I think a Groovy version of your code with closures might look something like this:
// Create a class that wraps CriteriaQuery and allows you
// to pass closures. This is wordy too, but at least it's
// reusable.
import org.neodatis.odb.impl.core.query.criteria;
class GroovyCriteriaQuery extends CriteriaQuery {
private final c;
QProspect(theClosure) {
// I prefer to check for null here, instead of in match()
if (theClosure == null) {
throw new InvalidArgumentException("theClosure can't be null!");
}
c = theClosure;
}
public boolean match(AbstractObjectInfo aoi){
//!! I'm assuming here that 'aoi' can be used as the actual
//!! object instance (or at least as proxy for it.)
//!! (You may have to extract the actual object from aoi before calling c.)
return c(aoi);
}
}
// Now use the query class in some random code.
Objects<Prospect> src = odb.getObjects(
new GroovyCriteriaQuery(
{ it.url.endsWith(".biz") }
)
)
I hope this helps!
I believe your real question is "Can I use closures instead of anonymous classes when calling Java APIs that do not use closures". And the answer is a definite "yes". This:
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
should work. You write "However, what groovy does it to associate the "match" method with the outer script context and fail me". How exactly does it fail? It seems to me like you're having a simple technical problem to get the solution that is both "the groovy way" and exactly what you desire to work.
Yep, thanks y'all, it works.
I also found out why SimpleNativeQuery does not work (per Dan Breslau).
I tried the following and it worked wonderfully. So the idiom does work as expected.
new File("c:\\temp").listFiles({ it.path.endsWith(".html") } as FileFilter);
This next one does not work because of the neodatis interface. The interface does not enforce a match() method! It only mentions it in the documentation yet it's not present in the class file:
public class SimpleNativeQuery extends AbstactQuery{
}
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
In the above, as the SimpleNativeQuery does not have a match() method, it makes it impossible for the groovy compiler to identify which method in the SimpleNativeQuery should the closure be attached to; it then defaults to the outer groovy script.
It's my third day with groovy and I'm loving it.
Both books are great:
- Groovy Recipes (Scott Davis)
- Programming Groovy (Venkat Subramaniam)

Resources