I'm trying Exception::Class for the first time and something that surprised me is that Exception::Class objects evaluate to true when returned from a function. Shouldn't the default be the opposite.
I know I can change this with overload but I am wondering if it's a good idea
sub gethtml{
return MyException->new( error => 'some error' );
}
my $response = &gethtml
if($response){
#do something with the html
}
else{
#something went wrong check if it's an exception object
}
You're confusing exceptions with returning a false value to indicate an error.
Part of the point of exceptions is that they provide their own channel to indicate error. This leaves return free to only return valid values. There's no need to check for false vs defined, or special objects, or do any per-function call error checking at all. It's all caught and dealt with at the end of the block.
If you return an exception object it defeats the point; they're not exceptions, they're just error codes.
To take advantage of exceptions, the code in your example should be written like this:
sub get_html {
...try to get the html...
return $html if defined $html;
MyException->throw( error => 'some error' );
}
eval {
my $html = get_html;
# do something with $html;
}
if ( my $e = Exception::Class->caught() ) {
...deal with the error...
}
This can be made a bit prettier with Try::Tiny.
This makes more sense when you have to do a lot of things which might error, such as a bunch of file, network or database operations. Look into modules such as autodie and Path::Tiny for how that works.
You should not create one with new and return it. They have a throw method that acts as constructor and die automatically.
use strict;
use warnings;
use Exception::Class qw( InputException HTTPException );
use Try::Tiny;
sub get_html {
my ($url) = #_;
# input validation
InputException->throw(error => 'no URL') unless $url;
my $res = $ua->get($url);
if ($res->is_success) {
# do more stuff with $res;
} else {
HTTPException->throw( error => 'request failed' );
}
}
# ... later
my $url;
try {
get_html($url);
} catch {
# handle the error which is in $_
if ( $_->isa('InputException') ) {
print "You need to supply a URL";
} elsif ( $_->isa('HTTPException') ) {
print "Could not fetch the HTML because the HTTP request failed.\n";
print "But I am not telling you why.";
}
}
You can then go and catch them (use Try::Tiny for that) or simply wrap it in an eval. But basically those exceptions are simple objects. They are intended as the return value of die and get thrown around, so there is no need to return them anywhere.
Once the program dies, all the scopes on the call stack are exited forcefully until you end up in an eval block (which is what catch does). There, you can handle the error. And since that error is an object, you can do fancy stuff with it.
+--------------------------------------------------------------------+
| sub { |
| +----------------------------------------------------------------+ |
| | if () { | |
| | +------------------------------------------------------------+ | |
| | | foo:: sub { | | |
| | | +--------------------------------------------------------+ | | |
| | | | catch { | | | |
| | | | +----------------------------------------------------+ | | | |
| | | | | doo_stuff:: sub { | | | | |
| | | | | +------------------------------------------------+ | | | | |
| | | | | | | | | | | |
| | | | | | MyException->throw ==> die $obj +---------------------------------+
| | | | | | do_more_stuff(); # never executed | | | | | | |
| | | | | | | | | | | | |
| | | | | +------------------------------------------------+ | | | | | |
| | | | +----------------------------------------------------+ | | | | |
| | | | | | | | |
| | | | handle_exception_in_catch($_) <---------------------------------+
| | | | # ( in Try::Tiny the exception ends up in $_ ) | | | |
| | | | | | | |
| | | +--------------------------------------------------------+ | | |
| | +------------------------------------------------------------+ | |
| +----------------------------------------------------------------+ |
+--------------------------------------------------------------------+
Also see the Exception::Class docs.
If you mix exceptions and regular die or Carp croak calls, you will have to do a lot of checking if stuff is blessed before using ->isa. Safe::Isa comes in handy here.
use strict;
use warnings;
use Exception::Class qw( InputException HTTPException );
use Try::Tiny;
use Safe::Isa;
sub get_html {
my ($url) = #_;
# input validation
InputException->throw(error => 'no URL') unless $url;
my $res = $ua->get($url);
if ($res->is_success) {
# do more stuff with $res;
die "There is no answer in this HTML" if $res->decoded_content !~ m/42/;
} else {
HTTPException->throw( error => 'request failed' );
}
}
With this code, the $_->isa('...') would blow up, because in case of the die call, $_ is not an object and you cannot call the method isa on an unblessed reference (or non-reference). Safe::Isa provides a $_isa, which checks for that first and otherwise just returns false.
my $url;
try {
get_html($url);
} catch {
# handle the error which is in $_
if ( $_->$_isa('InputException') ) {
print "You need to supply a URL";
} elsif ( $_->$_isa('HTTPException') ) {
print "Could not fetch the HTML because the HTTP request failed.\n";
print "But I am not telling you why.";
}
}
For details on how that works, see mst's talk You did what?
Related
Cross-reference causes memory leaking in Perl like this.
{
my #a = qw(a b c);
my #b = qw(a b c);
# both reference count are 1
push #a, \#b;
# #b reference count is 2(from #b and via #a)
push #b, \#a;
}
# #b reference count is 2(from via #a)
I understand memory leaking by cross-reference in this situation.
But the memory leaking can be resolve by explicit reassignment like this.
{
my #a = qw(a b c);
my #b = qw(a b c);
# both reference count are 1
push #a, \#b;
# #b reference count is 2(from #b and via #a)
push #b, \#a;
#a = ();
}
# why is #b reference count 0?
#a is lexical scope so I think even if there is no reassignment, #a's reference will be invalid but former cause memory leaking and later is not, why?
You start with
#a #b
| ARRAY | ARRAY
| REFCNT=2 | REFCNT=2
+-->+-----------+ +-->+-----------+
| | +-------+ | | | +-------+ |
| | | a | | | | | a | |
| | +-------+ | | | +-------+ |
| | | b | | | | | b | |
| | +-------+ | | | +-------+ |
| | | c | | | | | c | |
| | +-------+ | | | +-------+ |
| | | --------+ | | --------+
| | +-------+ | | +-------+ | |
| +-----------+ +-----------+ |
| |
+---------------------------------------+
If you were to exit the scope here, the reference counts would drop to one, and they would leak.
After #a = ();:
#a #b
| ARRAY | ARRAY
| REFCNT=2 | REFCNT=1
+-->+-----------+ +-->+-----------+
| | | | +-------+ |
| | | | | a | |
| | | | +-------+ |
| | | | | b | |
| | | | +-------+ |
| | | | | c | |
| | | | +-------+ |
| | | | | --------+
| | | | +-------+ | |
| +-----------+ +-----------+ |
| |
+---------------------------------------+
Note that #b's reference count went from two to one.
On scope exit, #a's reference count will drop to one, and #b's reference count will drop to zero.[1] This will free #b, which will cause #a's reference count to drop to zero. And that will free #a.
No cycle, so no memory leak.
At least in theory. In practice, what actually happens is a bit different as an optimization. But those are internal details that aren't relevant here.
i have this sources
first start.pl
use strict;
use warnings;
use hh;
print "Starting...\n";
my $abc={
VAL=>['alfa','beta'],
STUDENTS=>{
stud1=>{VAL=>['delta','gama']},
stud2=>{VAL=>['omega','upsilon']},
}
};
my $object=hh->new(catalog=>$abc);
and the package
package hh;
use strict;
use warnings;
sub new {
my $class=shift;
my $self={#_};
bless ($self,$class) ;
$self->_initialize("",['BEGIN']);
return $self ;
}
sub _initialize {
my $self=shift;
my $tab=shift;
my $carry=shift;
$tab=$tab."\t|";
if (defined $self->{VAL}){print "$tab Have val=",join(" ",#{$self->{VAL}}),"\n";push(#{$carry},#{$self->{VAL}})}
foreach my $k (sort {lc $a cmp lc $b} keys %{$self}){
print $tab,"carry=",$#{$carry}+1," "," ",$k,"\n";
if (ref($self->{$k}) eq 'HASH'){print "$tab Running initialize pentru $k\n";_initialize($self->{$k},$tab,$carry)}
}
return $self;
}
1;
the output is like this
Starting...
|carry=1 catalog
| Running initialize pentru catalog
| | Have val=alfa beta
| |carry=3 STUDENTS
| | Running initialize pentru STUDENTS
| | |carry=3 stud1
| | | Running initialize pentru stud1
| | | | Have val=delta gama
| | | |carry=5 VAL
| | |carry=5 stud2
| | | Running initialize pentru stud2
| | | | Have val=omega upsilon
| | | |carry=7 VAL
| |carry=7 VAL
Somehow i want to collect into an array the VAL arrays between top of tree and the walked element . Way the last VAL who is in the second level has 7 elements . I want to have only 2 elements (alfa and beta)
this is the expected output
Starting...
|carry=1 catalog
| Running initialize pentru catalog
| | Have val=alfa beta
| |carry=3 STUDENTS
| | Running initialize pentru STUDENTS
| | |carry=3 stud1
| | | Running initialize pentru stud1
| | | | Have val=delta gama
| | | |carry=5 VAL carry=5 (OK)
| | |carry=5 stud2
| | | Running initialize pentru stud2
| | | | Have val=omega upsilon
| | | |carry=7 VAL carry=5 (not ok this need only his values catalog alfa beta omega epsilon)
| |carry=7 VAL not ok (carry 3 only catalog alfa beta)
...
If you want to accumulate only the values in the current branch, you have to pass a copy of the carry array. [#$carry] dereferences the arrayref $carry and creates a new arrayref from the elements. I kept the original reference for the debug print at the end of the function. The more natural way would be to write $carry = [#$carry].
use strict;
use warnings;
package hh;
use Data::Dumper;
sub new {
my $class=shift;
my $self={#_};
bless ($self,$class) ;
$self->_initialize("",['BEGIN']);
return $self ;
}
sub _initialize {
my $self=shift;
my $tab=shift;
my $carry=shift;
$tab=$tab."\t|";
print "$tab _initialize() called with carry "," [",join(', ',#$carry),"]\n";
my $new_carry = [#$carry];
if (defined $self->{VAL}){
print "$tab Have found val=",join(" ",#{$self->{VAL}}),"\n";
print "$tab pushing ",join(" ",#{$self->{VAL}}),"\n";
push(#{$new_carry},#{$self->{VAL}})
}
print $tab," carry=",$#{$new_carry}+1," [",join(', ',#$new_carry),"]\n";
foreach my $k (sort {lc $a cmp lc $b} keys %{$self}){
if (ref($self->{$k}) eq 'HASH'){print "$tab Running initialize pentru $k\n";_initialize($self->{$k},$tab, $new_carry)}
}
print "$tab returning to previous level. carry was "," [",join(', ',#$carry),"]\n";
return $self;
}
package main;
print "Starting...\n";
my $abc={
VAL=>['alfa','beta'],
STUDENTS=>{
stud1=>{VAL=>['delta','gama']},
stud2=>{VAL=>['omega','upsilon']},
}
};
my $object=hh->new(catalog=>$abc);
This prints:
Starting...
| _initialized called with carry [BEGIN]
| carry=1 [BEGIN]
| Running initialize pentru catalog
| | _initialized called with carry [BEGIN]
| | Have found val=alfa beta
| | pushing alfa beta
| | carry=3 [BEGIN, alfa, beta]
| | Running initialize pentru STUDENTS
| | | _initialized called with carry [BEGIN, alfa, beta]
| | | carry=3 [BEGIN, alfa, beta]
| | | Running initialize pentru stud1
| | | | _initialized called with carry [BEGIN, alfa, beta]
| | | | Have found val=delta gama
| | | | pushing delta gama
| | | | carry=5 [BEGIN, alfa, beta, delta, gama]
| | | | returning to previous level. carry was [BEGIN, alfa, beta]
| | | Running initialize pentru stud2
| | | | _initialized called with carry [BEGIN, alfa, beta]
| | | | Have found val=omega upsilon
| | | | pushing omega upsilon
| | | | carry=5 [BEGIN, alfa, beta, omega, upsilon]
| | | | returning to previous level. carry was [BEGIN, alfa, beta]
| | | returning to previous level. carry was [BEGIN, alfa, beta]
| | returning to previous level. carry was [BEGIN]
| returning to previous level. carry was [BEGIN]
I am trying to send a TCP packet to a host and get a response and obviously not doing something right.
I've compared to many posts and even my Perl Cookbook and can't figure out where I'm going wrong. Very simply trying the following:
use strict;
use warnings;
use IO::Socket::INET;
$| = 1;
my $socket = new IO::Socket::INET (
PeerHost => '<the_host_name>',
PeerPort => '33792',
Proto => 'tcp'
);
unless($socket) {
print "couldn't connect to the server\n";
}
# data to send to a server
my $req = <<SBCEND;
START
AUTOLOGUE CENTRAL
TYPE|QUOTE|
H|1|1.0|Regular|CANCEL|6072|TESTDT|JOHNAIX| | |26000|DEMO| | | | | | | | |0
P|1|HAS|LF115|HAS|LF115|10| | | | | | | | | | | | | | |
P|2|WIX|51515|WIX|51515|24| | | | | | | | | | | | | | |
P|3|FRA|PH8A|FRA|PH8A|2| | | | | | | | | | | | | | |
END
SBCEND
my $message = "\002".$req."\003";
my $size = $socket->send($message);
shutdown($socket, 1);
my $response = "";
$socket->recv($response, 1024);
$socket->close();
print "sent: $size\n$req\n";
print "received response: $response\n";
I get no response at all, not sure if I'm supposed to or If something is wrong with my request.
When I ask their support if I'm supposed to get any response regardless, they send me the response I should receive from the docs if the request is valid.
The above request data is an example they sent to me. They did tell me I needed binary 2 at the beginning of my request and binary 3 after, hence my message variable above.
Can someone tell me if I'm doing something wrong here?
Turns out I needed a new line after the binary 2, a small change to this line:
my $message = "\002\r\n".$req."\003";
I am using the CPAN Text::Table module. I have a table in my script and some values are multi-line strings. I am also using a rule to print this table.
My code is as follows:
#!/usr/bin/perl5.14.1
use FindBin;
use lib "$FindBin::Bin/mylib/Text-Aligner-0.12/lib/";
use lib "$FindBin::Bin/mylib/Text-Table-1.130/lib/";
use Text::Table;
my $tb = Text::Table->new(\'| ', "", \' | ', "Field ", \'| ', "Length ", \'| ', "Comment ", \' |');
my #AoA = (
[ 1, "Foo", "20", "Foo" ],
[ 2, "Bar", "35", "Bar\nBar" ],
[ 3, "Tze", "10", "Tze\nTze" ],
);
$tb->load(#AoA);
my $rule = $tb->rule(qw/- /);
my #arr = $tb->body;
print $rule, $tb->title, $rule;
for (#arr) {
print $_ . $rule;
}
However when I run this I get the following:
|---|-------|--------|----------|
| | Field | Length | Comment |
|---|-------|--------|----------|
| 1 | Foo | 20 | Foo |
|---|-------|--------|----------|
| 2 | Bar | 35 | Bar |
|---|-------|--------|----------|
| | | | Bar |
|---|-------|--------|----------|
| 3 | Tze | 10 | Tze |
|---|-------|--------|----------|
| | | | Tze |
|---|-------|--------|----------|
Is there a way to not print separate lines in the case of multi-line strings?
I want to display my table as follows:
|---|-------|--------|----------|
| | Field | Length | Comment |
|---|-------|--------|----------|
| 1 | Foo | 20 | Foo |
|---|-------|--------|----------|
| 2 | Bar | 35 | Bar |
| | | | Bar |
|---|-------|--------|----------|
| 3 | Tze | 10 | Tze |
| | | | Tze |
|---|-------|--------|----------|
The Text::Table code calls split( /\n/ ) on each row of your data and puts the results into their own rows. You can work around this by calculating which rows actually correspond to multi-line data and only printing a rule at the appropriate boundaries:
use strict;
use warnings;
use List::Util qw(max);
use Text::Table;
my $sep = \'|';
my #col_spec = ($sep, '', $sep, 'Field', $sep, 'Length', $sep, 'Comment', $sep);
my $tb = Text::Table->new(#col_spec);
my #data = (
[ 1, "Foo", "20", "Foo" ],
[ 2, "Bar\nBaz\nQux", "35", "Bar\nBar" ],
[ 3, "Tze", "10", "Tze\nTze" ]
);
# Track which rows should be preceded by rules. A key of 'n' indicates that a
# rule should be printed before the nth row (zero-indexed).
my %indexes = (
0 => 1, # Before header row
1 => 1 # After header row
);
# Calculate body rows for which rules should be printed
my $count = 1;
foreach my $row (#data) {
# Greatest number of newlines in row
my $newlines = max map { tr/\n// } #$row;
# One newline corresponds to two rows
$count += $newlines + 1;
$indexes{$count} = 1;
}
$tb->load(#data);
my $rule = $tb->rule('-', '+');
foreach my $i (0 .. $tb->height) {
print $rule if exists $indexes{$i};
print $tb->table($i);
}
Output:
+-+-----+------+-------+
| |Field|Length|Comment|
+-+-----+------+-------+
|1|Foo |20 |Foo |
+-+-----+------+-------+
|2|Bar |35 |Bar |
| |Baz | |Bar |
| |Qux | | |
+-+-----+------+-------+
|3|Tze |10 |Tze |
| | | |Tze |
+-+-----+------+-------+
How do I get the path for the below option?
basiclly messages will be parsed to following dir "/tmp/msg-1370789006-11903-0" which is made up of
time and process ID , how do I get that into my varible for later use?
### Tell it where to put things:
$parser->output_under("/tmp");
The distro's overall documentation (also found in the distro's README file) contains the following useful information:
Overview of the classes
Here are the classes you'll generally be dealing with directly:
(START HERE) results() .-----------------.
\ .-------->| MIME:: |
.-----------. / | Parser::Results |
| MIME:: |--' `-----------------'
| Parser |--. .-----------------.
`-----------' \ filer() | MIME:: |
| parse() `-------->| Parser::Filer |
| gives you `-----------------'
| a... | output_path()
| | determines
| | path() of...
| head() .--------. |
| returns... | MIME:: | get() |
V .-------->| Head | etc... |
.--------./ `--------' |
.---> | MIME:: | |
`-----| Entity | .--------. |
parts() `--------'\ | MIME:: | /
returns `-------->| Body |<---------'
sub-entities bodyhandle() `--------'
(if any) returns... | open()
| returns...
|
V
.--------. read()
| IO:: | getline()
| Handle | print()
`--------' etc...
Which leads us to looking at MIME::Body's documentation, which include the following:
### Where's the data?
if (defined($body->path)) { ### data is on disk:
print "data is stored externally, in ", $body->path;
}
else { ### data is in core:
print "data is already in core, and is...\n", $body->as_string;
}
### Get rid of anything on disk:
$body->purge;