Blame lib/Class/ReturnValue.pm

Packit c9e8cb
use warnings;
Packit c9e8cb
use strict;
Packit c9e8cb
Packit c9e8cb
package Class::ReturnValue;
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=head1 NAME
Packit c9e8cb
Packit c9e8cb
Class::ReturnValue - A return-value object that lets you treat it 
Packit c9e8cb
as as a boolean, array or object
Packit c9e8cb
Packit c9e8cb
=head1 DESCRIPTION
Packit c9e8cb
Packit c9e8cb
Class::ReturnValue is a "clever" return value object that can allow
Packit c9e8cb
code calling your routine to expect:
Packit c9e8cb
    a boolean value (did it fail)
Packit c9e8cb
or  a list (what are the return values)
Packit c9e8cb
Packit c9e8cb
=head1 EXAMPLE
Packit c9e8cb
Packit c9e8cb
    sub demo {
Packit c9e8cb
        my $value = shift;
Packit c9e8cb
        my $ret = Class::ReturnValue->new();
Packit c9e8cb
        $ret->as_array('0', 'No results found');
Packit c9e8cb
    
Packit c9e8cb
        unless($value) {
Packit c9e8cb
            $ret->as_error(errno => '1',
Packit c9e8cb
                               message => "You didn't supply a parameter.",
Packit c9e8cb
                               do_backtrace => 1);
Packit c9e8cb
        }
Packit c9e8cb
Packit c9e8cb
        return($ret->return_value);
Packit c9e8cb
    }
Packit c9e8cb
Packit c9e8cb
    if (demo('foo')){ 
Packit c9e8cb
        print "the routine succeeded with one parameter";
Packit c9e8cb
    }
Packit c9e8cb
    if (demo()) {
Packit c9e8cb
        print "The routine succeeded with 0 paramters. shouldn't happen";
Packit c9e8cb
    } else {
Packit c9e8cb
        print "The routine failed with 0 parameters (as it should).";
Packit c9e8cb
    }
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
    my $return = demo();
Packit c9e8cb
    if ($return) {
Packit c9e8cb
        print "The routine succeeded with 0 paramters. shouldn't happen";
Packit c9e8cb
    } else {
Packit c9e8cb
        print "The routine failed with 0 parameters (as it should). ".
Packit c9e8cb
              "Stack trace:\n".
Packit c9e8cb
        $return->backtrace;
Packit c9e8cb
    }
Packit c9e8cb
Packit c9e8cb
    my @return3 = demo('foo');
Packit c9e8cb
    print "The routine got ".join(',',@return3).
Packit c9e8cb
          "when asking for demo's results as an array";
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
    my $return2 = demo('foo');
Packit c9e8cb
Packit c9e8cb
    unless ($return2) {
Packit c9e8cb
        print "The routine failed with a parameter. shouldn't happen.".
Packit c9e8cb
             "Stack trace:\n".
Packit c9e8cb
        $return2->backtrace;
Packit c9e8cb
    }
Packit c9e8cb
Packit c9e8cb
    my @return2_array = @{$return2}; # TODO: does this work
Packit c9e8cb
    my @return2_array2 = $return2->as_array;
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
use Exporter;
Packit c9e8cb
Packit c9e8cb
use vars qw/$VERSION @EXPORT @ISA/;
Packit c9e8cb
Packit c9e8cb
@ISA = qw/Exporter/;
Packit c9e8cb
@EXPORT = qw /&return_value/;
Packit c9e8cb
use Carp;
Packit c9e8cb
use Devel::StackTrace;
Packit c9e8cb
use Data::Dumper;
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
$VERSION = '0.55';
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
use overload 'bool' => \&error_condition;
Packit c9e8cb
use overload '""' => \&error_condition;
Packit c9e8cb
use overload 'eq' => \&my_eq;
Packit c9e8cb
use overload '@{}' => \&as_array;
Packit c9e8cb
use overload 'fallback' => \&as_array;
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=head1 METHODS 
Packit c9e8cb
Packit c9e8cb
=item new
Packit c9e8cb
Packit c9e8cb
Instantiate a new Class::ReturnValue object
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub new {
Packit c9e8cb
    my $self = {};
Packit c9e8cb
    bless($self);
Packit c9e8cb
    return($self);
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
sub my_eq {
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if (wantarray()) {
Packit c9e8cb
        return($self->as_array);
Packit c9e8cb
    }
Packit c9e8cb
    else {
Packit c9e8cb
        return($self);
Packit c9e8cb
    }    
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
=item as_array
Packit c9e8cb
Packit c9e8cb
Return the 'as_array' attribute of this object as an array.
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=item as_array [ARRAY]
Packit c9e8cb
Packit c9e8cb
If $self is called in an array context, returns the array specified in ARRAY
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub as_array {
Packit c9e8cb
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if (@_) { 
Packit c9e8cb
        @{$self->{'as_array'}} = (@_);
Packit c9e8cb
    }
Packit c9e8cb
    return(@{$self->{'as_array'}});
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=item as_error HASH
Packit c9e8cb
Packit c9e8cb
Turns this return-value object into  an error return object.  TAkes three parameters:
Packit c9e8cb
Packit c9e8cb
    message
Packit c9e8cb
    do_backtrace
Packit c9e8cb
    errno 
Packit c9e8cb
Packit c9e8cb
    'message' is a human readable error message explaining what's going on
Packit c9e8cb
Packit c9e8cb
    'do_backtrace' is a boolean. If it's true, a carp-style backtrace will be 
Packit c9e8cb
    stored in $self->{'backtrace'}. It defaults to true
Packit c9e8cb
Packit c9e8cb
    errno and message default to undef. errno _must_ be specified. 
Packit c9e8cb
    It's a numeric error number.  Any true integer value  will cause the 
Packit c9e8cb
    object to evaluate to false in a scalar context. At first, this may look a 
Packit c9e8cb
    bit counterintuitive, but it means that you can have error codes and still 
Packit c9e8cb
    allow simple use of your functions in a style like this:
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
        if ($obj->do_something) {
Packit c9e8cb
            print "Yay! it worked";
Packit c9e8cb
        } else {
Packit c9e8cb
            print "Sorry. there's been an error.";
Packit c9e8cb
        }
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
        as well as more complex use like this:
Packit c9e8cb
Packit c9e8cb
        my $retval = $obj->do_something;
Packit c9e8cb
        
Packit c9e8cb
        if ($retval) {
Packit c9e8cb
            print "Yay. we did something\n";
Packit c9e8cb
            my ($foo, $bar, $baz) = @{$retval};
Packit c9e8cb
            my $human_readable_return = $retval;
Packit c9e8cb
        } else {
Packit c9e8cb
            if ($retval->errno == 20) {
Packit c9e8cb
                die "Failed with error 20 (Not enough monkeys).";
Packit c9e8cb
            } else {
Packit c9e8cb
                die  $retval->backtrace; # Die and print out a backtrace 
Packit c9e8cb
            }
Packit c9e8cb
        }
Packit c9e8cb
    
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub as_error {
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    my %args = ( errno => undef,
Packit c9e8cb
                 message => undef,
Packit c9e8cb
                 do_backtrace => 1,
Packit c9e8cb
                 @_);
Packit c9e8cb
Packit c9e8cb
    unless($args{'errno'}) {
Packit c9e8cb
        carp "$self -> as_error called without an 'errno' parameter";
Packit c9e8cb
        return (undef);
Packit c9e8cb
    }
Packit c9e8cb
Packit c9e8cb
    $self->{'errno'} = $args{'errno'};
Packit c9e8cb
    $self->{'error_message'} = $args{'message'};
Packit c9e8cb
    if ($args{'do_backtrace'}) {
Packit c9e8cb
        # Use carp's internal backtrace methods, rather than duplicating them ourselves
Packit c9e8cb
         my $trace = Devel::StackTrace->new(ignore_package => 'Class::ReturnValue');
Packit c9e8cb
Packit c9e8cb
        $self->{'backtrace'} = $trace->as_string; # like carp
Packit c9e8cb
    }
Packit c9e8cb
Packit c9e8cb
    return(1);
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=item errno 
Packit c9e8cb
Packit c9e8cb
Returns the errno if there's been an error. Otherwise, return undef
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub errno { 
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if ($self->{'errno'}) {
Packit c9e8cb
        return ($self->{'errno'});
Packit c9e8cb
     }
Packit c9e8cb
     else {
Packit c9e8cb
        return(undef);
Packit c9e8cb
     }
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=item error_message
Packit c9e8cb
Packit c9e8cb
If there's been an error return the error message.
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub error_message {
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if ($self->{'error_message'}) {
Packit c9e8cb
        return($self->{'error_message'});
Packit c9e8cb
    }
Packit c9e8cb
    else {
Packit c9e8cb
        return(undef);
Packit c9e8cb
    }
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=item backtrace
Packit c9e8cb
Packit c9e8cb
If there's been an error and we asked for a backtrace, return the backtrace. 
Packit c9e8cb
Otherwise, return undef.
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub backtrace {
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if ($self->{'backtrace'}) {
Packit c9e8cb
        return($self->{'backtrace'});
Packit c9e8cb
    }
Packit c9e8cb
    else {
Packit c9e8cb
        return(undef);
Packit c9e8cb
    }
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
=item error_condition
Packit c9e8cb
Packit c9e8cb
If there's been an error, return undef. Otherwise return 1
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
sub error_condition { 
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if ($self->{'errno'}) {
Packit c9e8cb
            return (undef);
Packit c9e8cb
        }
Packit c9e8cb
        elsif (wantarray()) {
Packit c9e8cb
            return(@{$self->{'as_array'}});
Packit c9e8cb
        }
Packit c9e8cb
       else { 
Packit c9e8cb
            return(1);
Packit c9e8cb
       }     
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
sub return_value {
Packit c9e8cb
    my $self = shift;
Packit c9e8cb
    if (wantarray) {
Packit c9e8cb
         return ($self->as_array);
Packit c9e8cb
    }
Packit c9e8cb
    else {
Packit c9e8cb
       return ($self);
Packit c9e8cb
    }
Packit c9e8cb
}
Packit c9e8cb
Packit c9e8cb
Packit c9e8cb
=head1 AUTHOR
Packit c9e8cb
    
Packit c9e8cb
    Jesse Vincent <jesse@bestpractical.com>
Packit c9e8cb
Packit c9e8cb
=head1 BUGS
Packit c9e8cb
Packit c9e8cb
    This module has, as yet, not been used in production code. I thing
Packit c9e8cb
    it should work, but have never benchmarked it. I have not yet used
Packit c9e8cb
    it extensively, though I do plan to in the not-too-distant future.
Packit c9e8cb
    If you have questions or comments,  please write me.
Packit c9e8cb
Packit c9e8cb
    If you need to report a bug, please send mail to 
Packit c9e8cb
    <bug-class-returnvalue@rt.cpan.org> or report your error on the web
Packit c9e8cb
    at http://rt.cpan.org/
Packit c9e8cb
Packit c9e8cb
=head1 COPYRIGHT
Packit c9e8cb
Packit c9e8cb
    Copyright (c) 2002,2003,2005,2007 Jesse Vincent <jesse@bestpractical.com>
Packit c9e8cb
    You may use, modify, fold, spindle or mutilate this module under
Packit c9e8cb
    the same terms as perl itself.
Packit c9e8cb
Packit c9e8cb
=head1 SEE ALSO
Packit c9e8cb
Packit c9e8cb
    Class::ReturnValue isn't an exception handler. If it doesn't
Packit c9e8cb
    do what you want, you might want look at one of the exception handlers
Packit c9e8cb
    below:
Packit c9e8cb
Packit c9e8cb
    Error, Exception, Exceptions, Exceptions::Class
Packit c9e8cb
Packit c9e8cb
    You might also want to look at Contextual::Return, another implementation
Packit c9e8cb
    of the same concept as this module.
Packit c9e8cb
Packit c9e8cb
=cut
Packit c9e8cb
Packit c9e8cb
1;