Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Using a subroutine as a module

by bradcathey (Prior)
on Nov 02, 2003 at 23:36 UTC ( [id://303965]=perlquestion: print w/replies, xml ) Need Help??

bradcathey has asked for the wisdom of the Perl Monks concerning the following question:

Part of my "re-learn Perl the right way" journey.

Very simple, I want to use an external file containing a subroutine called by my main script. At the same time, I'm trying to get my head around variable scoping, so may need help there. Have searched the literature (Camel, Llama, Sheep and PM) and can't find a simple explanation of how to put all the pieces together in a working model. Anyway, here's what I have for my main:
#!/usr/bin/perl print "Content-type: text/plain\n\n"; use strict; use CGI::Carp qw(fatalsToBrowser); use validate; my $the_time = "12:23 pm"; &val_time( $the_time ); $the_time = $val; if ($the_time) { print "$the_time\n" } else { print "error\n" }
My external file ('validate.pm'):
#!/usr/bin/perl our $val; sub val_time { $val = shift; $val = ($val =~ /^(\d{2}):(\d{2}) (am|pm)$/) ? "$1:$2 $3" : 1; }
When I run it, I get validate.pm did not return a true value at timeval.pl line 7. No surprise, but what am I missing:

1. correct scoping? 2. correct set-up of external? 3. correct call of subroutine from main?

I figure it's easier than I think, just help get me on the right path. Thanks!

Replies are listed 'Best First'.
Re: Using a subroutine as a module
by The Mad Hatter (Priest) on Nov 02, 2003 at 23:46 UTC
    Whenever you include a module, that module must return true. The most common method for ensuring this is putting an always true value as the last line of code in the module. Ex/
    # My module ...code... 42;
    Just to point out, if you don't export subroutines on use in your module, than you have to use fully qualified variable names. For this, you need a package declaration in your module. At the top of validate.pm, put package MyModule; (or something similar) and then after use-ing the module from your script, call the sub like MyModule::val_time() and access the variable like $MyModule::val.
Re: Using a subroutine as a module
by Roger (Parson) on Nov 02, 2003 at 23:53 UTC
    Your module validate.pm *MUST* evaluate to true, ie., by adding 1; at the end of the module, or else it will not be loaded.

    Thus your module validate.pm should look like this-
    package MyValidate; # use package to declare a module our $val; sub val_time { $val = shift; $val = ($val =~ /^(\d{2}):(\d{2}) (am|pm)$/) ? "$1:$2 $3" : 1; } 1;
    Referring to a variable inside a package is not recommended. You could do the following instead -
    package MyValidate; # use package to declare a module sub val_time { my $val = shift; $val = ($val =~ /^(\d{2}):(\d{2}) (am|pm)$/) ? "$1:$2 $3" : 1; return $val; # yes this is not necessary but it clarifies my inten +sion } 1;
Re: Using a subroutine as a module
by ysth (Canon) on Nov 03, 2003 at 00:29 UTC
    The other answers so far have addressed your actual problem. I'd like to add a comment on your "validate.pm".

    You start it with a shebang line, which is an odd thing to do since it is not actually executed (nor will any flags such as -w be parsed from the line by perl).

    Usually (but only optionally) a module will declare its own name space and only export to the use'ing code subs that are requested:

    in MyModule.pm:

    package MyModule; use Exporter (); our @ISA = 'Exporter'; our @EXPORT_OK = qw/foo bar/; sub foo { "do some foo stuff" } sub bar { "do some bar stuff" } 1; # not actually needed since @EXPORT_OK= evaluates as true
    and in the calling code:
    use MyModule 'foo'; &foo();
    You might take a look at perlmod.pod for more info on creating modules.

    One last comment: one-level all-lowercase names like "validate" are usually for pragmas and are reserved for use by perl5-porters.

      Actually, better not to export at all:

      in MyModule.pm:

      package MyModule; sub new { bless {}, shift } sub foo { "do some foo stuff" } 1;
      and in the calling code:
      use MyModule; my $o = new MyModule; $o->foo();
      $o is an object. It can have data, but in this case, it is just a way to tell perl where to find the subroutine foo(). This means you could have several different modules, each with a routine named 'foo;, and by using the object each module returns (customarily) from the call to new(), you can tell them apart. Whereas if you have the misfortune to use 2 modules that both export foo into your namespace, who knows what happens?

      --Bob Niederman, http://bob-n.com

      All code given here is UNTESTED unless otherwise stated.

        I would rather use class methods than create a dummy object with no other purpose than to qualify the package to call.

        File::Spec is an excellent example of when that works well.

        In the more typical case, however, you would be just as well off importing nothing and always fully qualifying your calls (e.g. MyModule::foo() ). Importing some things is a programmer convenience to save typing and improve readability. If the sub names reflect what they do, conflicts should be rare (and can always be resolved by fully qualifying).

        Egads, far too many uses of OO are superfluous and/or ill advised already and now you want OO for simple library calls. Just use fully qualified package names:

        #### MyMod.pm package MyMod; sub foo { "whatever" } 1; ### calling script #!/usr/bin/perl -w use strict; use MyMod; print MyMod::foo();
Re: Using a subroutine as a module
by bradcathey (Prior) on Nov 03, 2003 at 02:11 UTC
    Wow, all great answers and a big help. I can hardly wait to get started. Thanks responsive monks!

    Update:
    It worked as shown below. Good stuff and sorely missing from the literature. Maybe this would make a good Q&A (now an unapproved question, under subroutines).
    #!/usr/bin/perl print "Content-type: text/plain\n\n"; use strict; use CGI::Carp qw(fatalsToBrowser); use Validate; my $the_time = "12:23 pm"; Validate::val_time($the_time); $the_time = $Validate::val; if ($the_time) { print "$the_time\n" } else { print "error\n" }
    And the module:
    package Validate; # use package to declare a module our $val; sub val_time { $val = shift; $val = ($val =~ /^(\d{2}):(\d{2}) (am|pm)$/) ? "$1:$2 $3" : 1; return $val; #worked with and without this line } 1;
    Thanks!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://303965]
Approved by Enlil
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (7)
As of 2024-04-19 09:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found