views:

118

answers:

4

I'd like to port a little piece of code from Ruby to Groovy, and I'm stuck at this:

def given(array,closure) {
    closure.delegate = array
    closure()
}

given([1,2,3,4]) {
   findAll { it > 4}
}

Right now it dies with this message:

Exception thrown: Cannot compare ConsoleScript0$_run_closure1 with value 'ConsoleScript0$_run_closure1@1e6743e' and java.lang.Integer with value '4'.

I tried to set the closure's delegate to be the array, but it seems that in the findAll method, it represents a closure, instead of an actual item from the array. I also tried to run the closure like this:

array.with {
   closure(array)
}

but I still wasn't able to make it work. Any thoughts on what could work? Ruby's equivalent would be to instance_eval the closure in the array's context.

EDIT: Running Mykola's code produced this output:

given [1, 2, 3, 4]
class Demo$_main_closure1
2
Exception thrown: Cannot compare Demo$_main_closure1 with value 'Demo$_main_closure1@fe53cf' and java.lang.Integer with value '2'

groovy.lang.GroovyRuntimeException: Cannot compare Demo$_main_closure1 with value 'Demo$_main_closure1@fe53cf' and java.lang.Integer with value '2'

    at Demo$_main_closure1_closure2.doCall(ConsoleScript3:15)

    at Demo$_main_closure1.doCall(ConsoleScript3:15)

    at Demo$_main_closure1.doCall(ConsoleScript3)

    at Demo.given(ConsoleScript3:28)

    at Demo$given.callStatic(Unknown Source)

    at Demo.main(ConsoleScript3:12)

I'm running Groovy 1.6.5.

+1  A: 

It looks like a bug for me. Here is the code

class Demo {
   static def main(args) {
      given([1, 2, 3, 4]) {
          println getClass()
          println size()  
          grep { v -> v > 2 }  
      }
   }

   static def size() {
      return 2
   }

   static def given(object, closure) {
       println 'given ' + object

       closure.resolveStrategy = Closure.DELEGATE_ONLY
       closure.delegate = object
       closure()
   }
}

Which had to print (I tend to think) '4' as a size. And actually it prints if you will comment method 'size'.

You can read about resolveStrategy more and then let us know what wasn't set properly.

Mykola Golubyev
I've updated my question with the output of your code. Still doesn't work.
Geo
Yes. That's what I am talking about. Either I don't understand 'DELEGATE_ONLY' option or there is a bug in 1.6.5. I will try 1.6.4 at work tomorrow.
Mykola Golubyev
Thanks! Please post back after you got the chance to do so.
Geo
Using Groovy 1.6.3 I get the same error as Geo
alexander.egger
According to http://groovy.codehaus.org/api/groovy/lang/Closure.html#setResolveStrategy(int) the resolve strategy only works for properties. As grep findAll etc are methods setting the resolveStrategy property does not solve the problem.
alexander.egger
the resolve strategy doesn't only work with properties, otherwise calling "println size()" in the closure wouldn’t work, but it does.This is a groovy bug and a JIRA ticket should be opened on it.def given(array,closure) { closure.resolveStrategy = Closure.DELEGATE_FIRST closure.delegate = array closure()}given([1,2,3,4,5]) { assert 5 == size() assert [5] == findAll { it > 4}}
Ted Naleid
+1  A: 

Simple - you are trying to call a closure passing it an array, where findAll should be called on the array itself.

Here are a couple of possible solutions. First one is straightforward:

def given(array,closure) {
    closure(array)
}

println "first way result: " +
given ( [1,2,3,4,5] ) { it.findAll { it > 4 } }

Or you can encapsulate findAll within a method body (that depends on what you are actually trying to do):

def given(array,closure) {
 array.findAll(closure)
}

println "second way result: " + 
given( [1,2,3,4,5] ) { it > 4 }

Here are the results of both:

first way result: [5]
second way result: [5]

Groove away!

litius
I know this is possible. I'd like to get my way to work.
Geo
+1  A: 

In this case the delegate object is a java.util.ArrayList object which does not have a forEach method.

Nevertheless the Groovy wrapper for this class has this method, but it is not used here (this seams to be a bug).

You can workaround this by using delegate.forEach(). I can see that this breaks the DSL you have in mind, but maybe it takes you a step closer.

The following code works for me:

def given(array,closure) {
    closure.delegate = array
    closure()
}

given([1,2,3,4]) {
   delegate.findAll { it > 4}
}
alexander.egger
A: 

Try:

array.with(closure)

Or if you want to keep your syntax:

def given(array,closure) {
    array.with(closure)
}
noah