I´ve been coding Perl for a couple years already, but now I feel it´s time to convert my Perl4-style custom libraries into real .pm modules. The main reason is because I´m implementing mod_perl and it seems Apache::Reload is only guaranteed to track updates on module-like 'libraries', called with use.
I´ve been through the manpages and also the Camel book, but the OO literature just feeks like Greek to me. Just can´t figure out how to do it! I get lost in the therminology real fast. So I wonder if you guys can give me and applied explanation, say, about how would be a module created after library that looks like this:
[mylibrary1.pl]
sub half {
my $number = shift;
return $number / 2;
}
The main issues are: require Exporter; @ISA = qw(Exporter); @EXPORT = qw(half);
I really thank you if you can help me out on this. Sorry if this is too OO-newbie, but I think it may be an interessting material for the community, as I couldn´t find any didatic explanation on this library-module conversion over the web.
Thanks a lot!
Andre
Old:
[mylibrary1.pl]
sub half {
my $number = shift;
return $number / 2;
}
New:
[mylibrary1.pm]
package mylibrary1;
use base 'Exporter';
use vars qw( @EXPORT @EXPORT_OK );
@EXPORT = qw( half ); # These are the ones that are imported if nothing is specified.
@EXPORT_OK = qw( half ); # These are the ones you may specify, if you want.
sub half {
my $number = shift;
return $number / 2;
}
1; # All .pm files must end with a true value
__END__
Now, when you use them, you'll do it like this:
Old way:
[myscript.pl] require 'mylibrary1.pl'; print half( 10 ), "\n";
New way:
[myscript.pl] use mylibrary1 qw( half ); print half( 10 ), "\n";
Now, the different between @EXPORT and @EXPORT_OK is how you construct the 'use' line. Here's a few examples:
use Foo; # Imports everything in @EXPORT, nothing in @EXPORT_OK use Foo 'bar'; # Imports 'bar' if it's in @EXPORT_OK. Imports nothing from @EXPORT use Foo (); # Imports NOTHING from either.
Using package::utilname(...); $utlobject->utl_method(...); is flexible enough and no real need to save few keystrokes by not pointing package name, and risking to have naming conflict later....
Also, extra dependency on Exporter, and many extra lines in your template to introduce Exporter, do not worth the effort.
Best regards,
Courage, the Cowardly Dog
Now, if the OP wants, we can educate him on better software practices later. But, for now, this solves the OP's problem to a 't'.
package MyModule;
sub new {
# if you call MyModule->new, $_[0] will be 'MyModule'
my $class = shift;
# $self can be any reference, but usually a hash
my $self = {};
bless $self, $class;
return $self;
}
sub half {
# if you call $m->half(10), $_[0] will be the object
my ( $self, $number ) = @_;
return $number / 2;
}
What happened in your case was that you tried to divide the first argument in @_ in your 'half' sub by two. However, if you use 'half' as a method call (i.e. $m->half(10)), the first argument is the $m object (sometimes called the 'invocant'), not the number 10. Hence, the return value (being whatever you pulled off $_[0]) is the object, which, when printed out, yields Package=REFTYPE(address), in your case Mymodule=HASH(0xbd5068).
If you're doing it the $m->half() way, then you need to have a module that looks like this:
package MyPackage;
sub new {
my ($class) = shift();
my $self = {};
return bless $self, $class;
}
sub half {
my $self = shift();
my $number = shift();
return $number / 2;
}
1;
and a script that looks like this:
use MyPackage; my $m = MyPackage->new(); print $m->half(10);Where you create a new MyPackage object and then use the object->function call to get the result.
($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
=~y~b-v~a-z~s; print
Say you create your module and export the half() function as before, you can have your function look like this:
sub half {
my $number;
if (ref($_[0]) eq 'MyPackage') {
my $self = shift();
$number = shift();
} else {
$number = shift();
}
return $number / 2;
}
so that, if called via $m->half(), it works the first way, but if called just via half() it works the second way.
($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
=~y~b-v~a-z~s; print
Instead of
sub half {
my $number;
if (ref($_[0]) eq 'MyPackage') {
my $self = shift();
$number = shift();
} else {
$number = shift();
}
return $number / 2;
}
We can make the adaptation of all perl4-ish functions possible just by adding a single line, on the top of each function's code:
sub half {
if (ref($_[0]) =~ /MyPackage/ ) { my $self = shift; }
# as the former Perl4-like functions don´t need the $self object, this line's role is to clean up the first element of the @_ array! And, so, make things ready for the shifts your code already is set to!
my $number = shift;
return $number / 2;
}
Detail: I call all my methods libraries MyPackage, Mypackage2 etc, so by testing with this regexp, I don't even have to worry to adapt this one-line according to the library I´m converting into module! Just adding it all over the subs, and finally enter the OO world!
Ugh, please don't do that. It's brittle to maintain, hard to document, confusing to explain, and I've never seen a case where it really simplifies things.
If you really need to export a simple procedural interface, create an object and export curried functions that use the object to call the methods.
For what it is worth - I really like that Perl6 will distinguish between methods and subs as well as provide multi-method dispatch.
Cheers - L~R
It could be as simple as:
use vars '@EXPORT';
@EXPORT = qw( list of methods to curry );
sub import
{
my $class = shift;
my $curried_object = $class->new();
my $caller = caller();
for my $export ( @EXPORT )
{
*{ $caller . "::$export" } = sub { $curried_object->$export( @_ ) };
}
}
sub half {
my $number;
if (ref $_[0] eq __PACKAGE__) {
my ( $self, $number ) = @_;
}
# uncuddles elses are faster!
else {
$number = $_[0];
}
return $number / 2;
}
Or even:
sub half {
my $number;
if (ref $_[0]) {
my ( $self, $number ) = @_;
}
# uncuddles elses are faster!
else {
$number = $_[0];
}
return $number / 2;
}
And then, if you're one of those suspenders + belt (just to be safe, you know) kind of people, you can always do looks_like_number $number and throw an exception (with baroque stack trace) on false.
perlmonks.org content © perlmonks.org and Andre_br, blahblah, chromatic, Cody Pendant, Courage, dragonchild, Limbic~Region, rvosa, wazoox
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03