Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a class attribute dynamically?

How can I add an attribute to a class dynamically? I tried this but it's complaining about a missing method and I'm not sure why since I'm not trying to add a method.

use v6.d;

class Foo does Metamodel::AttributeContainer {
    submethod BUILD(:$attr) {
        my $a = Attribute.new(:name($attr), :type(Str), :package(Foo));
        self.add_attribute(self, $a);
    }
}

my Foo $foo = Foo.new(:attr('bar'));
$foo.bar = 'baz';  # No such method 'bar' for invocant of type 'Foo'
say $foo.bar;
like image 918
gdonald Avatar asked Sep 18 '19 12:09

gdonald


1 Answers

There's no way to add an attribute to a class once it has been composed - that is, after its closing curly } has been parsed. In general, declarative things done using the metamodel need to be done at compile time.

About the code that you have written in the question:

  • Conceptually, attributes exist per class, not per object, so there's no way to make something like this work.
  • Even putting that aside, doing the Metamodel::AttributeContainer role won't help anything here; it's to be composed into a meta-class, which holds the metadata about attributes, rather than the class that has the attributes being declared.
  • The error about the method bar not being found is because an attribute accessor - even a generated one - is just an ordinary method.

It's hard to know what to suggest instead without knowing the problem you were trying to solve in the first place. Whatever it is, it can't be solved by trying to add attributes per-object. Perhaps consider either:

  • Using mixins, which are a means to changing an individual object
  • Having a class that contains a hash to store its data, and then using the FALLBACK method to resolve method calls into hash accesses for the valid keys
like image 62
Jonathan Worthington Avatar answered Sep 20 '22 16:09

Jonathan Worthington