Just to give a bit more background.

If you've ever looked at a PDF, it's mostly constructed of fairly
conventional data structures such as arrays, dictionaries, names (hash
keys), byte-stings and numbers. These mostly map reasonably well to Perl
6's built in types.

However, these are then organized into indirect objects, e.g.

3
 0 obj
<< /Type /Pages /Count 1 /Kids [ 4 0 R ] >>
endobj
4 0 obj
<< /Type /Page /MediaBox [ 0 0 420 595 ] >>

endobj



Which is roughly equivalent to:

my $Page = { :Type<Page>, :MediaBox[0, 0, 420, 595 ] };
my $Pages = { :Type<Pages> , :Count(1) };

Or at least should be this simple if we can hide indirect objects. These
only need to be handled at a low level when reading or writing to PDF's.

On top of that gradual typing can be used for validation. MediaBox for
example should always be present in Page of objects and is always an array.

At least that's the theory. I've got an experimental module under
construction over at https://github.com/p6-pdf/perl6-PDF-Tools.

Field access need to be flexible. It's sometimes the case that user code
will need to get at additional entries which are specified.

It's got a pretty simple tied interface to arrays and hashes, which are a
bit more evolved than the above.

hash methods are also overrride: AT-KEY ( $foo<bar>) , and ASSIGN-KEY
($foo<bar> = 42)

It's an experiment, which is so far, progressing pretty well, the above is
just a nit.
- David

On Fri, Jul 31, 2015 at 9:47 AM, David Warring <david.warr...@gmail.com>
wrote:

> 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