6
votes

This is similar to, but not quite the same as another of my questions: Content checking some, not all, class attributes

I am writing a test to verify the effect of processing on a object. But I can't work out how to get the value of a known set of the object's attributes but test with the same code. Something like the following (which does not work):

class A { has $.a, has $.b, has $.c };
my A $v .=new(:1a, :2b);

for <a b> { ok $v.{$_} > 0 }; # this does not work, but illustrates the intent.

If instead of an object I had used a Hash, then it is easy to get values from the Hash knowing the keys.

At the moment all I can think of is to test each attribute:

ok $v.a > 0;
ok $v.b > 0;

In addition, I don't want to look at ALL the attributes of the object, which is what .^attributes gives me, only some of them.

1
You can make your not-working example work quite simply: for <a b> { ok $v."$_"() > 0 };. Would that solve your problem? You could also implement the Associative role to make your sample work as-is. - Tyil

1 Answers

5
votes

The issue you are encountering – and the reason tests like this are easier with Hashes than with Classes – is that you are passing in a Str when you need to pass in an object. This issue doesn't come up with Hashes because (by default), Hash keys already are strings.

The other important thing to remember is that, technically speaking, there isn't any way to directly access the attributes of a Raku object from outside of the object – Raku does not have public attributes, just attributes you can access from a public accessor method. This may seem like a pedantic distinction, but it's important here: it means that you need a callable Method object (which is more specific than the "object" I mentioned in the previous paragraph).

As Tyil mentioned in a comment, you can get Raku to dwym with the code for <a b> { ok $v."$_"() > 0 };. The reason this works is that the final () forces Raku to treat the interpolated value of "$_" as a Callable, which in this case means a Method.

However, I'd prefer to be slightly more explicit and use the following syntax:

for $v.^methods.grep({.name ~~ 'a' | 'b'}) { ok $_($v) > 0}

(This takes advantage of the fact that $object.method and method($object) are syntactically equivalent).

Note that we have the grep above because you wanted to test just some of A's methods. However, even if you want to test all of the methods you added to A, you may still need the grep to exclude BUILDALL, which is returned by $v.^methods(:local). I haven't figured out whether that is intentional or a bug (it doesn't seem like a local method) and would appreciate anyone's thoughts on that.