Search code examples
grailsgroovygroovyshell

How can I pass a class to Groovy's Eval binding?


I'm doing some gross stuff, like using Groovy's metaClass and Eval to dynamically assign properties to an object from an XML import:

class ObjectBuilder {
  def assignProp(String propName, String propValue) {
    Eval.x(this, "x.metaClass.${propName} = '${propValue}'")     
  }
}

def h = new ObjectBuilder()
h.assignProp('name', 'Henson')
println(h.name)

What I'd like to do though, is be able instantiate another copy of the class inside itself:

Eval.x(this, "x.metaClass.${ObjName} = new ObjectBuilder()")

But I can't, because I think the class isn't passed to the binding. Is there another solution?


Solution

  • A couple of solutions:

    Expando

    You may try working with a bunch of Expandos:

    h = new Expando()
    h.last = new Expando()
    h.last.name = 'tech'
    
    assert h.last.name == 'tech'
    

    Metaclass the object directly

    xml = '''
    <person>
      <name>john</name>
      <surname>doe</surname>
      <age>41</age>
      <location>St Louis, MO</location>
    </person>
    '''
    
    class Person {
      def name, surname, location, age
    }
    
    root = new XmlParser().parseText xml
    
    person = new Person(root.children().collectEntries { [it.name(), it.text()] })
    
    person.metaClass.getFullname = { "$delegate.name $delegate.surname" }
    
    assert person.fullname == "john doe"
    
    person.metaClass.likes = "chicken with lemon"
    
    assert person.likes == "chicken with lemon" 
    

    Maps

    map = [:]
    map.last = [:]
    map.last.name = 'Tech'
    assert map.last.name == 'Tech'