Ayman
Ayman

Reputation: 11585

Groovy dynamic property per object

Using Groovy 1.8. I'm trying to create a dynamic class definition that will cache properties per object. I did use propertyMissing without adding the property to the object just fine. I just think caching the properties would be more efficient. Right?

Note that each instance must have its own different properties.

The code below works fine:

class C {}
def c = new C()
c.metaClass.prop = "a C property"
println c.prop

def x = new C()
x.prop

will output:

a C property
groovy.lang.MissingPropertyException: No such property: prop for class: C

If I need to this problematically:

class A {
    def propertyMissing(String name) {
        if(!this.hasProperty(name)) {
            println "create new propery $name"
            this.metaClass."$name" = "Dyna prop $name"
            println "created new propery $name"
        }
        this.metaClass."$name"
    }
}

a = new A()
println a.p1

For A, I get as far as "create new property", but the line this.metaClass."$name" = "Dyna prop $name" fails with: No such property: p1 for class at line 5

Whats wrong?

Upvotes: 5

Views: 16755

Answers (3)

Angel O'Sphere
Angel O'Sphere

Reputation: 2666

Why don't you store your dynamic properties in a simple HashMap?

class Foo {
    def storage = [:]
    def propertyMissing(String name, value) { storage[name] = value }
    def propertyMissing(String name) { storage[name] }
}
def f = new Foo()
f.foo = "bar"

This is the standard example from: http://groovy.codehaus.org/Using+methodMissing+and+propertyMissing

Note, you don't have to check if the property is really missing ... if it won't be missing, the method would not be called in the first place.

Upvotes: 6

tim_yates
tim_yates

Reputation: 171054

This code should do what you want:

class A {
  A() {
    def mc = new ExpandoMetaClass( A, false, true)
    mc.initialize()
    this.metaClass = mc
  }

  def propertyMissing( String name ) {
    println "create new propery $name"
    def result = "Dyna prop $name"
    this.metaClass."$name" = result
    println "created new propery $name"
    result
  }
}

a = new A()
println a.p1
println a.p1

That outputs:

create new propery p1
created new propery p1
Dyna prop p1
Dyna prop p1

Upvotes: 8

Manny
Manny

Reputation: 6287

ExpandoMetaClass is not really designed to be used after initialize or instance variables (see reference below at least for 1.6 or older).

Maybe you want to use Runtime mixins. More information here and here.

You may want to read a blogpost, which a workaround is provided to define properties at runtime. And the related groovy bug (that is not really a bug) related to it.

I saw in the comments section and verified at least in version 1.6 ExpandoMetaClass documentation:

By default methods are only allowed to be added before initialize() is called. In other words you create a new ExpandoMetaClass, add some methods and then call initialize(). If you attempt to add new methods after initialize() has been called an error will be thrown.

Upvotes: 1

Related Questions