Quick introduction here so uninterested parties can continue on, this is about not having code in .pl scripts and instead breaking it out into a module and calling the module as the only line in the 'script'

This meditation was written by request, from someone who had seen my short Re: use and require inside subs post on this subject in reply to a question regarding use and require inside subs use and require inside subs. This person was curious about overhead involved and what the .pl files end up looking like.

Perl Scripts are Code Too

I believe that all code should be tested. That being the case, having a .pl script with a bunch of code in it that uses my nicely tested modules just feels wrong. I use Devel::Cover as a guide to how much of my code I've bothered to test, and unless all my code is available to my test suite I do not truly know that my code is tested.

Know When to Say When

Of course, doing this for anything under (say) 20 lines of code is probably not worth the effort, but then that doesn't fall under my category of serious scripts either. This is not to knock some of the awesome things in 20 lines, but more to acknowledge that there are situations where it isn't really much use.

Modules aren't that heavy

So let's look at what it takes to take a script and make it a module. Say I have this script, with foo and bar subs

#!/usr/bin/perl sub foo { } sub bar { } foo(bar(@ARGV));

Obviously this falls under my 20 line rule but I'm trying to show how it's done here ;)

The first step is to copy the .pl to a .pm file, in the appropriate namespace. I'll pretend that I use the Mischief namespace for all of my modules, so I put this script in Mischief/Scripts/foobar.pm

package Mischief::Scripts::foobar; use strict; use base 'Exporter'; our @EXPORT = qw/run_script/; sub foo { } sub bar { } sub run_script { foo(bar(@ARGV)); } 1;

Then, we can cut the original .pl script down to

#!/usr/bin/perl use strict; use Mayhem::Script::foobar qw/run_script/; run_script();

And we get the nice benefit of having our script code being an integrated part of the code base, which makes refactoring much easier as well as having benefits for testability and code reuse.

Replies are listed 'Best First'.
Re: There Are No Perl Scripts
by dragonchild (Archbishop) on Nov 12, 2004 at 02:36 UTC
    You have just described the entire model that CGI::Application uses. In fact, one of the nagging design "issues" that C::A has is that every C::A child has to have1 a .cgi that looks something like:
    #!perl -T use strict; use warnings; $|++; use My::CGI::Application::Child; My::CGI::Application::Child->new->run;

    Also, this ends up being very similar to how Tk applications work. You have a little bit of setup, then you call MainLoop(), which does everything else for you. OO programming purists end up with something similar, but not as pretty. :-p

    1. Well, until you could use CGI::Application::Dispatch and CGI::Application::Apache to make things a little cleaner.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      Not that I do this myself currently, but by placing the initialization code inside your module in the 'non-subroutine' area (and using 'import' for initialization parameterization), you could strip it down to

      #!/usr/bin/perl -T use My::CGI::Application::Child ('initialization' => 'parameters');
Re: There Are No Perl Scripts
by exussum0 (Vicar) on Nov 11, 2004 at 21:56 UTC
    The biggest reason I do this, at times, is in a cgi environment. I put all my logic in modules that are outside of the cgi directory. One day, if someone screws up the mime type for .pl or .cgi or whatever I am using, the worst you get are where my modules are stored and the names of them.. plus a function inside.

    Hopefully, my webserver is setup correctly to never allow access to those directories. I can put in the extra precaution in my httpd.conf (apache) to disallow access to taht directory.. just in case someone breaks mime types. Won't work in a jail environment, unless I put them in my cgi directory AND put the disallow.

    Anyway, maybe it's just being paranoid or a security nut. :)

    ----
    Then B.I. said, "Hov' remind yourself nobody built like you, you designed yourself"

Re: There Are No Perl Scripts
by chb (Deacon) on Nov 12, 2004 at 07:02 UTC
    Minor style issue: if you put run_script into @EXPORT anyway, you don't have to mention it on the use line, it'll be exported by default. Some people think modules exporting stuff by default is questionable style (namespace pollution), so they would call run_script fully qualified: Mayhem::Script::foobar::run_script();

      Yes, you are absolutely correct. Usually I just use EXPORT_OK as one of my guiding lights is that package namespaces should be as clean as possible (and no cleaner). However, even when I do use EXPORT, I still make an effort to explicitly mention the function on my use line - not because I have to, but because it cuts down on garbage in my namespace (not that anyone besides me cares) as well as saving me the confusion of where foo() comes from later on when I'm reading my code from six months ago that was apparently written by a demented baboon.

Re: There Are No Perl Scripts
by wfsp (Abbot) on Nov 11, 2004 at 20:36 UTC
    Mischief and Mayhem!
    Most of my modules turn out like that.
    Thanks tuppence, I'm just about to venture into these waters myself. Very useful.
Re: There Are No Perl Scripts
by dimar (Curate) on Nov 12, 2004 at 18:27 UTC

    Do you use OOP style code in your modules? If not, what convention do you use when you want one module to *inherit* from another module?

    The difficulty (for me) encountered while trying to use inheritance and composition with non-OOP style modules is the only deficiency/annoyance I could anticipate with using this methodology on a wide scale.

      A large portion of my modules are indeed Object Oriented.

      I don't really see how that matters here though, and if I'm not using OOP I can't see wanting one module to inherit from another, as that is a core OOP-ism

      I have, however, run into times where I want the functionality of a non-OO module to be accessable in a OO fashion. In these cases, I usually write a OO wrapper for the module, in my namespace, making liberal use of Class::MethodMaker, that I then extend as needed.

      My advice when you need to do this is to keep the OO 'wrapper' module as light as possible (meaning it doesn't do anything the non-OO module doesn't do) and to extend that wrapper with yet another module that does what you need. This lets you keep pure-wrapper code away from your changes, and gives you another module to be happy that Devel::Cover gives you 100% on covering

Re: There Are No Perl Scripts
by Anonymous Monk on Nov 12, 2004 at 18:04 UTC
    If you want to write a test program that is somewhat different than the "main" program, it's usually very beneficial to put all of your program logic in a module. I'm doing this more and more as time goes on. Modules help me read less code at once, and I'm all for that.