I try to create an attribute trait. The use case is to mark some attributes of a class as "crudable" in the context of an objects-to-documents-mapping while other are not.
role crud {
has Bool $.crud is default(True);
}
multi trait_mod:<is>(Attribute $a, crud, $arg) {
$a.container.VAR does crud($arg);
}
class Foo {
has $.bar is rw;
# Provide an extra nested information
has $.baz is rw is crud(True);
}
By reading and adapting some example code, I managed to get something that seems to do what I want. Here is a snippet with test case.
When I instantiate a new Foo
object and set the $.bar
attribute (that is
not crud
), it looks like that:
.Foo @0
├ $.bar is rw = 123456789
└ $.baz is rw = .Scalar+{crud} @1
└ $.crud +{crud} = True
What I understand from this is that the $.baz
attribute got what I call a meta-attribute that is independent from its potential value.
It looks good to me (if I understood correctly what I did here and that my traits use is not a dirty hack). It is possible to reach $foo.baz.crud
that is True
. Though, I don't understand very well what .Scalar+{crud}
means, and if I can set something there and how.
When I try to set the $.baz
instance attribute, this error is returned:
Cannot modify an immutable Scalar+{crud} (Scalar+{crud}.new(crud => Bool::True))
in block <unit> at t/08-attribute-trait.t line 30
Note: This is the closest thing to a working solution I managed to get. I don't need different crud settings for different instances of instantiated Foo classes.
I never want to change the value of the boolean, in fact, once the object instantiated, just providing it to attributes with is crud
. I am not even interested to pass a True
or False
value as an argument: if it would be possible to just set the boolean trait attribute to True
by default, it would be enough. I didn't manage to do this though, like:
multi trait_mod:<is>(Attribute $a, :$crud!) {
# Something like this
$a.container.VAR does set-crud;
}
class Foo {
has $.bar is rw;
has $.baz is rw is crud;
}
Am I trying to do something impossible? How could I adapt this code to achieve this use case?
There are several things going on here. First of all, the signature of the trait_mod
looks to be wrong. Secondly, there appears to be a bad interaction when the name of a trait is the same as an existing role. I believe this should be an NYI exception, but apparently it either goes wrong in parsing, or it goes wrong in trying to produce the error message.
Anyways, I think this is what you want:
role CRUD {}; # since CRUD is used as an acronym, I chose to use uppercase here
multi trait_mod:<is>(Attribute:D $a, :$crud!) { # note required named attribute!
$a.^mixin: CRUD if $crud; # mixin the CRUD role if a True value was given
}
class A {
has $.a is crud(False); # too bad "is !crud" is invalid syntax
has $.b is crud;
}
say "$_.name(): { $_ ~~ CRUD }" for A.^attributes; # $!a: False, $!b: True
Hope this helps.