OK, so I have had problems with stringing methods for a while.

See I often deal with trees, for example (completely not at random!): XML::Twig. And when I want to grab part of the tree near where I am I can either use a simili-XPath expression, but that's often a tad verbose, or just do $text= $elt->first_child( 'subelt')->first_child( 'subsubelt')->text. This works fine if both the subelt and the subsubelt elements exist. If either of then does not, then one of the first_child methods returns undef and Perl dies, letting me know that it does not like at all calling methods on undef. The problem is that most of the time I really don't care whether those elements exist or not. If there's something at the end of the string of method calls fine, otherwise I just want to get undef and get on with my... process.

As it is the only solution is to write:

$text= ( $elt->first_child( 'subelt') && $elt->first_child( 'sub_elt') +->first_child( 'subsubelt')) ? $elt->first_child( 'sub_elt')->first_child( 'subsubelt')->text : undef +;

Gross!

The ideal solution would be for Perl to return (if asked politely, through a pragma for example) undef for any method call on undef. Or to be able to bless undef. Pity you can't do that!

So here is the solution I found: instead of returning undef I return an "invalid object": an object blessed in a different package. This package has 4 methods:init is called by packages that want to use the feature, and gets a list of methods that will be "protected", new of course, which creates the object, a hasref which just holds the name of the class of the initial object (a blessed scalar would work actually, but lets not get into those!), is_valid which returns always undef, and AUTOLOAD, which returns the object if the method is protected or undef if it is not. This way I can still call methods on the previously undefined value returned.

This is not a perfect solution however. First it does not deal with inheritance, a class that inherits from foo still needs to call the init method with the list of foo methods to protect, yuck!. Then it is probably slow as it uses AUTOLOAD, even if the goal here is convenience, not efficiency. Also users of the objects cannot test whether the result is undefined anymore, they have to use a is_valid method.

Hence my question: can I do better? Can this method be improved to be more transparent, at least for the user? Is there an alternate method that works better? Has anyone already done something similar?

Here is the code:

#!/bin/perl -w use strict; package invalid; our $protected; # class1 => method_a => 1 # => method_b => 1 # class2 ... our $AUTOLOAD; # called by the package using the protection # methods to protect are stored for each class sub init { my( $class, $class_to_protect, @methods_to_protect)= @_; my %methods_to_protect=map { ($_, 1) } @methods_to_protect; $protected->{$class_to_protect} = \%methods_to_protect; } # an invalid object stores the cass of the original object sub new { my( $class, $obj)= @_; return bless { class => ref $obj}, $class; } # obviously it is not valid, that's the whole point! sub is_valid { return undef; } # return the invalid object or undef sub AUTOLOAD { my $invalid= shift; my $method= ( split /::/, $AUTOLOAD)[-1]; # get the method from $ +AUTOLOAD my $class= $invalid->{class}; # the original class fr +om the object if( $protected->{$class}->{$method}) { return $invalid; } # protected method else { return undef; } # unprotected one } package foo; # need to pass the list of methods to protect BEGIN { invalid->init( __PACKAGE__, qw( kid)); } sub new { my( $class, $value)= @_; return bless { value => $value}, $class; } sub add_kid { my( $self, $kid)= @_; $self->{kid}= $kid; } sub value { my $self= shift; return $self->{value}; } sub kid { my $self= shift; return $self->{kid} if( $self->{kid}); return invalid->new( $self); # do not return undef but a +n invalid object } sub is_valid { return $_[0]; } package main; my $parent= foo->new( 'parent'); my $kid= foo->new( 'kid'); $parent->add_kid( $kid); print "foo:\n"; print " parent: ", $parent->value || '', "\n"; print " parent->kid: ", $parent->kid->value || '', "\n"; print " parent->kid->kid: ", $parent->kid->kid->value || '', "\n"; print " parent->kid->kid->kid: ", $parent->kid->kid->kid->value || '' +, "\n\n";

In reply to Stringing method calls by mirod

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.