The example below is combines traits (S14)  Lvalue subs (S06) to create a
base class SimpleTiedHash that ties Attributes to the hash using the
'Entry' accessor.

PDFCatalog is an example instance class.

This mostly works well. But SimpleTiedHash currently has a hacky way of
overriding the built-in accessor.

-- The :entry trait detects then overrides the Attribute `has_accessor
property, setting it back to false. This `fools` Perl6 class system into
not generating a standard accessor.

-- The SimpleTiedHash.compose method only needs to be invoked once per
class instance initialization, but is currently getting called too late and
repeatedly via the  new method.

Is there a better way of adding custom accessors from traits, just once
when classes are being initialized?

Code sample below
- David
==================================
my role TiedAtt {
    has Bool $.is-entry = True;
    has Bool $.gen-accessor is rw;
    has method has_accessor { False }
}

multi trait_mod:<is>(Attribute $att is rw, :$entry!) is export(:DEFAULT) {
    # fool rakudo into not generating a standard accessor

    my $gen-accessor = $att.has_accessor;
    $att does TiedAtt;
    $att.gen-accessor = $gen-accessor;
}

class SimpleTiedHash
    is Hash {
    # base class

    sub att-accessor($obj, Str $key, Attribute $att) {
        Proxy.new(
            FETCH => method {
                $obj{$key};
            },
            STORE => method ($val is copy) {
                # simple typecheck

                die "$key: {$val.perl} has wrong type"
                    unless $val ~~ $att.type;
                $obj{$key} = $val;
            });
    }

    # create accessors

    method compose {
        for self.^attributes.grep({ .name ~~ /^'$!'<[A..Z]>/ &&
.can('is-entry'\
) }) -> $att {
            my $key = $att.name.subst(/^'$!'/, '');
            warn "setting up attribute $key";
            if $att.gen-accessor &&  ! self.^declares_method($key) {
                $att.set_rw;
                self.^add_method( $key, method {
                    att-accessor(self, $key, $att ) } );
            }
        }
    }


    method new(|c) {
        my $obj = callsame;
        # happening too late.

        # should ideally be invoked once during class initialisation

        warn "composing...";
        $obj.compose();
        $obj;
    }
}

class PDFCatalog
    is SimpleTiedHash {
     # data instance class


    has Str $.Version is entry;
    has Hash $.Pages is entry;
    method ping{say 42}


}

my $cat = PDFCatalog.new;
$cat.ping;
$cat.Version = '1.3';
$cat.Pages = { :Count(0), :Kids[] };

say $cat.Version;
say $cat<Version>;
say $cat.Pages.perl;

Reply via email to