perl.beginners
[Top] [All Lists]

Make it as hard as possible for an app to run

Subject: Make it as hard as possible for an app to run
From: Steve Bertrand
Date: Thu, 25 Jun 2009 09:11:38 -0400
Newsgroups: perl.beginners
Hi everyone,

I'm at a point where I'm learning so much so quickly, that I'm in a
stage of analysis paralysis. In one particular case, I'm trying to
evaluate whether I should completely toss one of my own modules, and
replace it with Exception::Class. I'm sorry for the length of the post,
but if you do get a chance to browse it, I'm looking for a 'better' way
to do what I'm after.

In essence, what I am looking for is a sure-fire way to ensure/track
that when certain class/object methods are called, it has been put
through the Sanity class, and if the input data is invalid, a
descriptive error message, the original data and a *complete* stack
trace are available. I also need a fail-stop way to prevent things from
proceeding if the next guy after I get hit by a bus forgets to check for
an error condition.

An example. I have a 'Transaction' module that creates different types
of transaction objects. These objects are passed into a 'Sanity' module,
to ensure that before the transaction is processed and written
permanently into the accounting system, the data actually conforms to
certain standards.

In cases where these transactions are automated (hundreds per minute), I
needed a way to die gracefully, save the data, an error message for
which data was corrupt, and a stack trace, so I created an 'Error'
module, so here's how things work:

- Transac accepts data and an Error object from the calling application
- Transac passes data and the Error to Sanity
- Sanity validates the data. If the data is bad, it calls
$error->whatever(), which stores some information in itself. There is no
return from Sanity
- Transac checks whether $error->exists() is set, and if so, updates the
Error with more information. If exists() is not set, things progress norally

In order to get a complete stack trace, I have to perform this in every
call back down the stack. Because of the nature of the data, I MUST
ensure that Error is available and checked in ALL calls in the process.
Here is how I do that:

sub create_transaction {

        use Eagle::Sanity;

        my $class       = shift;
        my $data        = shift;
        my $line_num    = 1;
        my $error       = pop;

        # let $error die() us if required

        unless (defined $error) {
                $error = Eagle::Error->new();
                $error->bad_api();
        }

        my $self = {};
        bless $self;

        my $sanity = Eagle::Sanity->new;

        $sanity->transaction_data($data, $error);
        my $failure = $error->exists();

        if ($failure) {
                $error->add_trace();
                return();
        }

        while (my ($key, $value) = each %{$data}) {
                $self->{"line${line_num}"}{$key} = $value;
        }

        return $self;
}

The common pieces of code that are inserted into every call in the stack
are:

        my $error       = pop;
        unless (defined $error) {
                $error = Eagle::Error->new();
                $error->bad_api();
        }

        my $failure = $error->exists();
        if ($failure) {
                $error->add_trace();
                return();
        }

Steve
<Prev in Thread] Current Thread [Next in Thread>
  • Make it as hard as possible for an app to run, Steve Bertrand <=