Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Best way to track an error string in module?

by linxdev (Sexton)
on Mar 04, 2021 at 19:56 UTC ( [id://11129127]=perlquestion: print w/replies, xml ) Need Help??

linxdev has asked for the wisdom of the Perl Monks concerning the following question:

I have written a number of modules that make use of storing an error string to be called by the caller for retrieval. I am running my code through Perl::Critic and I think I need to refactor this idea to make it more "The Right Way(tm)." Below is some pseudo code with some sample methods.
package Object; sub new { my $class = shift; my $self = { 'ERRSTR' => undef }; bless $self, $class; return $self; } sub _errstr { return $_[0]->{'ERRSTR'}; } sub _login { my $self = shift; eval { ... $self->{'CONNECTION'} = .... }; if($@) { $self->{'ERRSTR'} = $@; return 1; } return 1; } sub _ping { my $self = shift; eval { if(not $self->{'CONNECTION'}) { $self->login() or die $self->errstr(); } .... }; if($@) { $Self->{'ERRSTR'} = $@; return 0; } return 1; } # This allows me to control nesting so that debug statements are inden +ted by 1 space so that # I can easily see the flow if I call Object->set_debug(1) from the ap +plication. AUTOLOAD { our $AUTOLOAD; my $request = undef; my $funcs = { 'ping' => sub { _ping(@_); }, .. }; if($AUTOLOAD =~ m/^Object::(.+?)$/) { $request = $1; foreach my $ref (keys %{ $funcs }) { next unless $ref eq $request; ... increment debug indention ... my $val = $funcs->{$ref}->($@); ... decrement debug indention .... return $val; } } die "Method ${AUTOLOAD} does not exist." }
Many methods call other object methods as needed. This particular module use SOAP::Lite to connect to a web server. I've abstracted all SOAP code from the application and reduced it to something like this:
use Object. sub main { eval { my $service = Object->new("127.0.0.1", .....); $service->ping() or die $service->errstr(); }; if($@) { warn "$@"; return 1; } return 0; } exit main;
Every time I want the application to be as few lines as possible and simple I'll write a module that will abstract the real module and do maintenance tasks. I do this most often with DBI where I'll target a SQLite file. The application will call methods that will manipulate data in the database. The module will check the integrity of the db, create it if it does not exist, vacuum if needed, etc. I use the same module with $self->{'ERRSTR'} As I convert the die() to croak() in Object I'm concerned that my method of writing the object is not compatible with Carp's stacktrace.

Replies are listed 'Best First'.
Re: Best way to track an error string in module?
by LanX (Saint) on Mar 04, 2021 at 21:09 UTC
    As far as I understand you want to use AUTOLOAD° as a wrapper around your methods to control some debug behavior.

    May I suggest that you use real wrappers?

    You can automatically replace all methods in a list with new ones which call the old ones internally.

    my $old_ref = \&meth; *meth = sub { before(); &$old_ref ; after() };

    No more code, because I'm not sure if that's your case, search for "Perl monkey-patching" if interested.

    AFAIK do some OOP models like Moose provide extra functionality for such "upgrades".

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    °) and as I explained in my other reply, using AUTOLOAD in classes is a tough choice leading to a lot of problems.

Re: Best way to track an error string in module?
by LanX (Saint) on Mar 04, 2021 at 20:42 UTC
    Sorry I don't get yet what you are trying to achieve ...

    ... but using AUTOLOAD inside OOP is frowned upon.

    See for instance Perl Best Practices where the problem of delegating between different AUTOLOADs in the inheritance tree is described.

    You'll also need an explicit sub DESTROY to avoid crazy AUTOLOAD calls when your object reaches it's end of life.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      To appease PErl::Critic I have to replace die with croak and I want to be sure that what I'm doing in the object is the best way to it and maintain the simplicity of the application. In the code below, I've calledping() without logging in so it will login for me. The auth tokens timeout so ping() will re-login if a token has timed out due to inactivity.
      /usr/bin/perl-MObject -le 'my $cIn the past, I'd keep track of my = O +bject->new("192.168.1.63"); $c->ping() or die $c->errstr();' soap:Server, invalid login/password at -e line 1. at -e line 1.
      The AUTOLOAD needs to go too. In the past I'd keep track of indention within each function. This is tedious and often forgotten, but indented debug trace makes reading easy for me. The debug id done simliar to this and this is mostly just pseudo:
      # Print Debug output sub p_debug { my $self = shift; return unless $self->{'DEBUG'}; printf "DEBUG: [%04f] %s%s\n", delta_time($last), (' ' x $indent), s +printf(shift @_, @_); return } # Th old way of tracking indent forced me to make sure I was increment +ing and decrementing when required. I was looking for a way to autom +atically increment on entry and automatically decrement on return sub ping { my $self = shift; $self->{'INDENT'}++; .... $self->{'INDENT'}--; return $result; }
      /usr/bin/perl-MObject -le 'my $cIn the past, I'd keep track of my = O +bject->new("192.168.1.63"); $c->ping() or die $c->errstr();' DEBUG: 0.0033 Object::echo(): sending data to soap server to be echoed + back. DEBUG: 0.0034 Object::ping(): pinging SOAP interface. DEBUG: 0.0275 Object::login(): authenticating on service. DEBUG: 1.5343 Object::login(): token 828609a7e43e4ce22131694bed3f6a7 +1. DEBUG: 1.5346 Object::login(): authenticated. DEBUG: 2.9340 Object::ping(): SOAP interface up. DEBUG: 3.1558 Object::echo(): complete.
      echo() called ping() first, ping() logged in, and then echo() returned back to caller.
        IMHO the already described technique of monkeypatching wrappers is the best general approach, it's also easily reservable with localized *globs.

        Concerning this special tracing Perl offers various approaches for that, like activating it in debugger-context.

        It should also be possible to tweak Carp to adjust intendation or to use an alternative Carp from CPAN.

        Changing your classes for that special need only looks like overkill to me and smells a lot like an XY Problem.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11129127]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-03-29 12:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found