Given this oversimplified XML file:
<Foo>Bar</Foo>
And this code which extracts the value for the Foo
element:
use XML::Rabbit;
use Data::Dump::Tree;
class RunInfo does XML::Rabbit::Node {
has $.foo is xpath("/Foo");
}
sub MAIN ( $file! ) {
my $xml = RunInfo.new( file => $file );
dump $xml;
put "-----------------------";
put "Foo is $xml.foo()";
}
You'll see that the value for foo
is Nil
, even though the output shows Foo is Bar
:
.RunInfo @0 ├ $.foo = Nil ├ $.context is rw = .XML::Document @1 │ ├ $.version = 1.0.Str │ ├ $.encoding = Nil │ ├ %.doctype = {0} @2 │ ├ $.root = .XML::Element @3 │ │ ├ $.name is rw = Foo.Str │ │ ├ @.nodes is rw = [1] @4 │ │ │ └ 0 = .XML::Text @5 │ │ │ ├ $.text = Bar.Str │ │ │ └ $.parent is rw = .XML::Element §3 │ │ ├ %.attribs is rw = {0} @7 │ │ ├ $.idattr is rw = id.Str │ │ └ $.parent is rw = .XML::Document §1 │ ├ $.filename = example.xml.Str │ └ $.parent is rw = Nil └ $.xpath is rw = .XML::XPath @9 ├ $.document = .XML::Document §1 └ %.registered-namespaces is rw = {0} @11 ----------------------- Foo is Bar
(Disclaimer: I came across this behavior today in my code, so I wrote it up Q & A style. Other answers welcome.).
By the way, here are links to XML::Rabbit and Data::Dump::Tree.
It is lazy, like many things in Perl 6. In other words, it intentionally doesn't waste time figuring out what the foo
attribute is unless you ask for it. This is an optimization that avoids consuming computational resources unless you need them.
If you dump the data structure after calling the foo
method, you'll see that it is populated in the data dump:
use XML::Rabbit;
use Data::Dump::Tree;
class RunInfo does XML::Rabbit::Node {
has $.foo is xpath("/Foo");
}
sub MAIN ( $file! ) {
my $xml = RunInfo.new( file => $file );
put "Foo is $xml.foo()";
dump $xml;
}
Foo is Bar .RunInfo @0 ├ $.foo = Bar.Str ├ $.context is rw = .XML::Document @1 │ ├ $.version = 1.0.Str │ ├ $.encoding = Nil │ ├ %.doctype = {0} @2 │ ├ $.root = .XML::Element @3 │ │ ├ $.name is rw = Foo.Str │ │ ├ @.nodes is rw = [1] @4 │ │ │ └ 0 = .XML::Text @5 │ │ │ ├ $.text = Bar.Str │ │ │ └ $.parent is rw = .XML::Element §3 │ │ ├ %.attribs is rw = {0} @7 │ │ ├ $.idattr is rw = id.Str │ │ └ $.parent is rw = .XML::Document §1 │ ├ $.filename = example.xml.Str │ └ $.parent is rw = Nil └ $.xpath is rw = .XML::XPath @9 ├ $.document = .XML::Document §1 └ %.registered-namespaces is rw = {0} @11
This is not the result of a built-in Perl 6 feature, but rather something the XML::Rabbit
module does.
That module provides the is xpath
trait, and makes sure that at class composition time, any attribute which has that trait applied gets its accessor method overridden with a custom one.
The custom accessor method calculates and sets the value for the attribute the first time it is called, and on subsequent calls simply returns the value that's now already stored in the attribute.
The custom accessor method is implemented as follows (taken from the module's source code with parts elided):
method (Mu:D:) {
my $val = $attr.get_value( self );
unless $val.defined {
...
$val = ...;
...
$attr.set_value( self, $val );
}
return $val;
}
Here, $attr
is the Attribute
object corresponding to the attribute, and was retrieved prior to installing the method using the Meta-Object Protocol (MOP).
The Data::Dump::Tree
module, in turn, doesn't use the accessor method to fetch the attributes's value, but rather reads it directly using the MOP.
Therefore it sees the attribute's value as Nil
if has not yet been set because the accessor was not yet called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With