Re: Initialization Impasse

[email protected] (Stevan Little)
Newsgroups perl.moose
Message-ID <[email protected]>
Bernardo,

On May 8, 2008, at 1:12 PM, [email protected] wrote:
> I've been playing with Moose in the last few months, writing an  
> audio file
> management suite of modules, and I am in awe of and in love with  
> it. It's
> one of the best things that have happened to Perl in the last few  
> years.
> Congratulations and thanks to all involved in its creation!

Thanks :)

> I have a couple of questions, which I'll try to put to you in separate
> posts, so as not make things too confusing. Here is the first one.
>
>   - uid() tickles the (lazy) tag attribute every time it's called,  
> when it's
> only needed the first time. I could forego the tag attribute's  
> lazyness,
> but that can be wasteful too, in case the uid and the tag are not used
> (initializing the tag requires the creation of a taglib file  
> object, and
> then calling a method, and some other processing). I think what I  
> need here
> is a way to test (in before_uid() ) whether the tag has already been
> initialized, in which case I can return without doing anything). Is  
> there a
> way to test for initializedness of an attribute?

Yes, take a look at the predicate option, it is currently only  
documented in Class::MOP::Attribute, but you use it like this:

has 'foo' => (is => 'rw', predicate => 'has_foo');

If foo has never been set, then $obj->has_foo will return false, if  
foo has been set (even with undef) then it will return true.

> - To work around infinite recursion (if uid() called tag(), which  
> calls
> uid() ), I set the uid value directly, but that assumes a hash-based
> object. Is there a cleaner or moosier way of setting the uid  
> attribute?

Hmm, Moose can't really help this. I suggest breaking the cycle at  
some point, or if you cant, looking into a proxy object I wrote one  
for Bread::Board recently (http://search.cpan.org/~stevan/Bread- 
Board-0.03/lib/Bread/Board/Service/Deferred.pm).

- Stevan


> This may well be an XY problem, where I think I need something I don't
> really need, so I'm attaching some pruned code below for more details.
>
> Bernardo
>
>
> =====================================================================
>
> package Audiotheque::AudioFile;
> use Moose;
> use MooseX::Types::Path::Class qw(File);
> with 'Audiotheque::Tag';
> with 'Audiotheque::Transcode';
>
> has 'path' => ( is => 'ro', isa => File, coerce => 1, required => 1,
>                 writer => '_set_path' );
>
> has 'uid' => ( is => 'rw', isa => 'Maybe[Str]', lazy => 1,
>     default => undef );
>
> sub save_tag { }  # Required by Audiotheque::Tag
>
> =====================================================================
>
> package Audiotheque::AudioFile::Ogg::Vorbis;
> use Moose;
> use Moose::Autobox;
> use MooseX::ClassAttribute;
> extends 'Audiotheque::AudioFile';
> use Audio::TagLib::Ogg::Vorbis::File;
>
> has '+tag' => (
>     default => sub {
>         my ($self) = @_;
>
>         # _normalFields() comes from Audiotheque::Tag role
>         my $tag = $self->_normalFields($self->_taglibTag);
>         $self->_move_uidFromTag($tag);
>
>         return $tag;
>     },
> );
>
> # Insist on moving UID to its own attribute, if it appears in a tag  
> argument
> after 'tag' => sub {
>     my ($self, $tag) = @_;
>
>     $self->_move_uidFromTag($tag) if defined $tag;  # Only on write
> };
>
> # Vorbis doesn't have a built-in UID, like the MD5 signature of  
> FLAC, so
> # it has to store it in a Vorbis comment. Because the uid has a  
> special
> # status, it's given its own accessor (read-only), and shouldn't be
> # accessible from the (more generic) tag method. Too many gates to
> # guard. To this end, if a UID is found in the tags (which come  
> from the
> # Vorbis comments), it is copied to the uid attribute and deleted from
> # the tags.
> #
> sub _move_uidFromTag {
>     my ($self, $tag) = @_;
>
>     if ( $tag->exists('UID') ) {
>         if ( $tag->at('UID')->[0]->defined ) {
>             # FIXME assumes hash-based obj. Direct access is to avoid
>             # infinite recursion when tickling tag from uid.
>             $self->{uid} = $tag->at('UID')->[0];
>         }
>         $tag->delete('UID');
>     }
> }
>
> #has '+uid' => (builder => '_build_uid');
> #
> #sub _build_uid { }
> #
> #after '_build_uid' => sub { $_[0]->tag }
>
> # Give a tickle to the (lazy) tag attribute, in case it hasn't been  
> set yet.
> # FIXME This happens on every uid access, but it's really needed  
> only for
> # the very first one. Quite a waste of sub calls. Is there a way
> # to test for "initializedness" (as opposed to definedness)?
> before 'uid' => sub {
>     my ($self, $uid) = @_;
>
>     $_[0]->tag if not defined $uid;  # Tickle only on attribute read.
> };
>
> has '_taglibFile' => (
>     isa       => 'Maybe[Audio::TagLib::File]',
>     lazy      => 1,
>     default   => sub { Audio::TagLib::Ogg::Vorbis::File->new(
> $_[0]->path ) },
>     accessor  => '_taglibFile',
>     clearer   => '_clear_taglibFile',
>     predicate => '_has_taglibFile',
>     handles   => { save => 'save' },
> );
>
> # Required by Audiotheqhe::AudioFile class <- Audiotheque::Tag role
> override 'save_tag' => sub {
>     my $self = shift;
>     [update taglibFile comments from tag]
>     $self->save;
> };
>
> sub _taglibTag {
>     my($self) = @_;
>     my %tag = [get comments from _taglibFile]
>     return \%tag;
> }
>
> =====================================================================
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.