views:

253

answers:

3

Let's say I have a Perl file in which there are parts I need to run only when I'm called as a script. I remember reading sometime back about including those parts in a main() method and doing a

main() unless(<some condition which tests if I'm being used as a module>);

But I forgot what the condition was. Searching Google hasn't turned out anything fruitful. Can someone point out the right place to look for this?

+12  A: 

If the file is invoked as a script, there will be no caller so you can use:

main() unless caller;

See brian d foy's explanation.

#!/usr/bin/perl

use strict;
use warnings;

main() unless caller;

sub main {
    my $obj = MyClass->new;
    $obj->hello;
}

package MyClass;

use strict;
use warnings;

sub new { bless {} => shift };

sub hello { print "Hello World\n" }

no warnings 'void';
"MyClass"

Output:

C:\Temp> perl MyClass.pm
Hello World

Using from another script:

C:\Temp\> cat mytest.pl
#!/usr/bin/perl

use strict;
use warnings;

use MyClass;

my $obj = MyClass->new;
$obj->hello;

Output:

C:\Temp> mytest.pl
Hello World
Sinan Ünür
This is really helpful. Thanks for the link.
seth
@seth you are welcome.
Sinan Ünür
What about `perl -MMyClass -e' ... '`?
Brad Gilbert
@Brad Gilber When MyClass.pm is written this way, `perl MyClass.pm` does the same thing as `perl -MMyClass -e 'main()'`
Sinan Ünür
@Brad: read the link. I show examples with -MModule :)
brian d foy
+3  A: 

Better to not do this, and instead take a structured approach like MooseX::Runnable.

Your class will look like:

class Get::Me::Data with (MooseX::Runnable, MooseX::Getopt) {

    has 'dsn' => (
        is            => 'ro',
        isa           => 'Str',
        documentation => 'Database to connect to',
    );

    has 'database' => (
        is         => 'ro',
        traits     => ['NoGetopt'],
        lazy_build => 1,
    );

    method _build_database {
        Database->connect($self->dsn);
    }

    method get_data(Str $for_person){
        return $database->search({ person => $for_person });
    }

    method run(Str $for_person?) {
        if(!$defined $for_person){
            print "Type the person you are looking for: ";
            $for_person = <>;
            chomp $for_person;
        }

        my @data = $self->get_data($for_person);

        if(!@data){
            say "No data found for $for_person";
            return 1;
        }

        for my $data (@data){
            say $data->format;
        }

        return 0;
    }
}

Now you have a class that can be used inside your program easily:

my $finder = Get::Me::Data->new( database => $dbh );
$finder->get_data('jrockway');

Inside an interactive script that is bigger than just the "run" method above:

...
my $finder = Get::Me::Data->new( dsn => 'person_database' );
$finder->run('jrockway') and die 'Failure'; # and because "0" is success
say "All done with Get::Me::Data.";
...

If you just want to do this standalone, you can say:

$ mx-run Get::Me::Data --help
Usage: mx-run ... [arguments]
    --dsn     Database to connect to

$ mx-run Get::Me::Data --dsn person_database
Type the person you are looking for: jrockway
<data>

$ mx-run Get::Me::Data --dsn person_database jrockway
<data>

Notice how little code you wrote, and how flexible the resulting class is. "main if !caller" is nice, but why bother when you can do better?

(BTW, MX::Runnable has plugins; so you can easily increase the amount of debugging output you see, restart your app when the code changes, make the app persistent, run it in the profiler, etc.)

jrockway
+1 for showing a Moose way. I still haven't made the investment to get into that mindset.
Sinan Ünür
+4  A: 

I call these things "modulinos" originally in my Scripts as Modules article for The Perl Journal (now Dr. Dobbs). Google that term and you get the right resources. Sinan already linked to my development sources for one of my books where I talk about it. You might also like How a Script Becomes a Module.

brian d foy