Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl - Package/Module Issues

From everything I've read on using Perl modules, the basic usage is:

  • Module file with .pm extension, which includes the statement package <name>, where <name> is the filename of the module without the extension.
  • Code file that uses module contains the statement use <name>;.

The application I'm coding has one main code script which uses about 5 modules. I had forgotten to include the package <name> statement in the modules, but my code still ran just fine with the use <name> statement. I started receiving Undefined subroutine errors with one of the modules, so I added the package statement to each of the modules. Now the rest of those modules stopped working. What gives?

Example:

mainapp.pl

#!/usr/bin/perl
use UtyDate;
my $rowDate = CurrentDate("YYYYMMDD");

UtyDate.pm

#!/usr/bin/perl
package UtyDate;
sub CurrentDate
{
    #logic
}
return 1;

When I run the above code, I get the error Undefined subroutine &main::CurrentDate called at.... However, if I remove the package UtyDate; line from UtyDate.pm, I get no error. This situation exists for several but not all of my modules.

There's obviously a lot more code I'm not showing, but I'm confused how any of the code I'm not showing could affect the package/use constructs I've shown here.

like image 943
brydgesk Avatar asked Jun 04 '10 20:06

brydgesk


People also ask

What is the difference between package and module in Perl?

A Perl package is a collection of code which resides in its own namespace. Perl module is a package defined in a file having the same name as that of the package and having extension . pm. Two different modules may contain a variable or a function of the same name.

What is CPAN module in Perl?

The Comprehensive Perl Archive Network (CPAN) is a repository of over 250,000 software modules and accompanying documentation for 39,000 distributions, written in the Perl programming language by over 12,000 contributors.

Where is @INC in Perl?

Perl interpreter is compiled with a specific @INC default value. To find out this value, run env -i perl -V command ( env -i ignores the PERL5LIB environmental variable - see #2) and in the output you will see something like this: $ env -i perl -V ... @INC: /usr/lib/perl5/site_perl/5.18.


3 Answers

When you use a module, the code in the module is run at compile time. Then import is called on the package name for the module. So, use Foo; is the same as BEGIN { require Foo; Foo->import; }

Your code worked without the package declarations because all the code was executed under the package main, which is used by the main application code.

When you added the package declarations it stopped working, because the subroutines you defined are no longer being defined in main, but in UtyDate.

You can either access the subroutines by using a fully qualified name UtyDate::CurrentDate(); or by importing the subroutines into the current name space when you use the module.

UtyDate.pm

package UtyDate;
use strict;
use warnings; 

use Exporter 'import';

# Export these symbols by default.  Should be empty!    
our @EXPORT = ();

# List of symbols to export.  Put whatever you want available here.
our @EXPORT_OK = qw( CurrentDate  AnotherSub ThisOneToo );

sub CurrentDate {
    return 'blah';
}

sub AnotherSub { return 'foo'; }

Main program:

#!/usr/bin/perl
use strict;
use warnings; 

use UtyDate 'CurrentDate';

# CurrentDate is imported and usable.    
print CurrentDate(), " CurrentDate worked\n";

# AnotherSub is not
eval {  AnotherSub() } or print "AnotherSub didn't work: $@\n";

# But you can still access it by its fully qualified name
print UtyDate::AnotherSub(), " UtyDate::AnotherSub works though\n";

See Exporter docs for more info.

like image 61
daotoad Avatar answered Oct 21 '22 23:10

daotoad


You are missing the exporter perl header code. You will need to add something like the following to the top of your pm file below the package statement:

package UtyDate;
BEGIN {
  use Exporter ();
  use vars qw($VERSION @ISA @EXPORT);
  $VERSION = "1.0.0";
  @ISA = qw(Exporter);
  @EXPORT = qw( &CurrentDate );
}

See this link: http://perldoc.perl.org/Exporter.html#DESCRIPTION

like image 24
Gray Avatar answered Oct 21 '22 22:10

Gray


Alternatively to Gray's suggestion, you can do this:

use UtyDate;
UtyDate::CurrentDate(...);
like image 1
Ryley Avatar answered Oct 21 '22 22:10

Ryley