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

I am an experienced Perl programmer, but I have not done a lot of object programming except for this type of recent perl module: workers.pm
package tom; sub new { my $self = "This is tom"; bless ($self, $class); } # (other subroutines for tom go here) package dick; sub new { my $self = "This is dick"; bless ($self, $class); } # (subroutines for dick go here) package harry; sub new { my $self = "This is harry"; bless ($self, $class); } # (routines for harry go here) 1;
So I use 1 PM file and create 3 different objects that an outside script can use and the package xxx; statement seems to work to separate the different objects.

My new task:

I need to write a single-file perl script that can be copied to a virgin Linux box and when run, it will scan through lots of stuff to sanity-check the system. Things like users/directories/services/etc.

I want to create a "validate_users" object that I can pass various names and options to and it will validate or explain what is wrong.

Then I want to create a "validate_directory" object that will do similar things for different directories.

But - I want these, and the main code, to all live in a single .pl file to make it easy to pass around and use. (Imagine running this on 800+ different linux box's - each with only standard RedHat linux perl installed).

I have tried creating a .pl file and the keyword "package" throws errors. I have tried creating a .pm file, then putting main code (even "print 'Hello World', before the "1;", and this throws different errors.

I am getting the feeling that I am trying to do something "against the rules", but thought I would ask the Monks before giving up and writeing linear code.

What is the correct syntax to create tom/dick/harry objects all in a single .pl file and then use them?

Replies are listed 'Best First'.
Re: Can I do single-file, Object oriented perl?
by ikegami (Patriarch) on Jul 05, 2005 at 16:32 UTC
    I have tried creating a .pl file and the keyword "package" throws errors. I have tried creating a .pm file, then putting main code (even "print 'Hello World', before the "1;", and this throws different errors.

    That makes no sense. If it works in the .pm, it'll work in the .pl. Another error must have been introduced. (It sounds like a runaway quote or a runaway block to me.)

    package AverageJoe; sub get_name { ${shift(@_)} } package tom; our @ISA = 'AverageJoe'; sub new { my $class = shift; my $text = "This is tom"; return bless(\$text, $class); } package dick; our @ISA = 'AverageJoe'; sub new { my $class = shift; my $text = "This is dick"; return bless(\$text, $class); } package main; # Not mod_perl safe. my $tom = tom->new(); my $dick = dick->new(); { print $tom->get_name(), $/; print $dick->get_name(), $/; }

    I had to make changes since 1) you weren't blessing a reference, and 2) you used $class without declaring or initializing it.

Re: Can I do single-file, Object oriented perl?
by jeffa (Bishop) on Jul 05, 2005 at 16:33 UTC

    All you need to do is define the main space with package main; ... you can see an example of that here: XML::Simple + Class::MethodMaker.

    Are you sure, however, that you really want to bless scalars like that? You need to bless the reference, not the scalar itself. Perhaps you meant something like this instead:

    my $self = { message => "This is dick", }; bless ($self, $class);

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    

      bless(\$text, $class); is blessing a reference - and because bless returns the blessed value as well as acting directly upon it, the subroutine *does* return a blessed reference. See for example:

      my $foo = "bar"; print bless \$foo, "Test";

      Update: Apologies to jeffa, I got confused and was reading the wrong code when replying. The OP is indeed trying to bless a non-ref scalar, which would lead to an error: Can't bless non-reference value

      /J\

Re: Can I do single-file, Object oriented perl?
by trammell (Priest) on Jul 05, 2005 at 16:40 UTC
    One trick I learned here for making a module that you can "run":
    package MyPackage; # if invoked from the command line, call run() with # command-line input __PACKAGE__->run( @ARGV ) unless caller();
    I have tried creating a .pl file and the keyword "package" throws errors. I have tried creating a .pm file, then putting main code (even "print 'Hello World', before the "1;", and this throws different errors.
    You'll have to be more specific; I do those things all the time without a hitch.
Re: Can I do single-file, Object oriented perl?
by davidrw (Prior) on Jul 05, 2005 at 16:51 UTC
    • Do you have the access to distribute a custom rpm for this script? That way you could package whataver you want, and it's easy to make sure all the servers have the same version.
    • Alternatively, take a look at PAR.
    • Alternatively, you could just dump a tarball over.. would be executed remote with a series of commands like (untested--verify the tar syntax):
      scp foo.tar.gz user@host:/tmp ssh user@host "rm -rf /tmp/foo/" ssh user@host "tar cvfz /tmp/foo.tar.gz -C /tmp" ssh user@host "perl -I/tmp/foo/modules /tmp/foo/bar.pl"
    • Alternatively, have your script use LWP (if avail) or wget or lynx or curl or scp to suck down a tarball of deps/tools/utils it needs from a central server of yours.
      Those are all some good thoughts, and I have even thought of writing the utility to run from a central location and use Net::SSH::Perl to do all the checks.

      But my boss wants a single script that can be used to "sanity-check" a new system. This includes basic stuff like permissions on directories, mount points, etc., and more complex stuff like SSH keys.

      It is a utility script that simply prints out a report of problems found. You delete it after you fix all the issues.

Re: Can I do single-file, Object oriented perl?
by bart (Canon) on Jul 05, 2005 at 16:47 UTC
    package tom; sub new { my $self = "This is tom"; bless ($self, $class); }
    That's very wrong. You should only bless references. This piece of code here is bound to fail.

    I have tried creating a .pl file and the keyword "package" throws errors.
    Are you sure you're not trying to run it with perl4?
      That's very wrong. You should only bless references. This piece of code here is bound to fail.

      Apologies. Of course my production code does something like this:

      my $self = { user_name => 'fubar', num_days => 0, }; bless ($self, $class);

      I threw the tom/dick/harry example together from memory. I wanted to emphasize that I was defining 3 classes in the same file - something that other OO languages do not like.

      If it works in the .pm, it'll work in the .pl. Another error must have been introduced.

      Ikegami - I think you got it - but with one exception:

      You have to include shebang (#!/usr/bin/perl) at the top of a .pl, but not at the top of a .pm file.

      I took my working .pm file and tried to get it to work as a .pl and it threw errors. But I did not notice the error messages were BASH error messages instead of Perl messages. (duh)

      I put shebang at the top of the script - and now it actually works. Thanks Monks!

        If it works in the .pm, it'll work in the .pl. Another error must have been introduced. Ikegami - I think you got it - but with one exception: You have to include shebang (#!/usr/bin/perl) at the top of a .pl, but not at the top of a .pm file.
        Just to nitpick a tad, the shebang has nothing to do with the difference between a ".pl" and a ".pm". Perl sees no difference between files, whatever their extension happens to be. The only practical difference is that 'use' and 'require' will append a .pm to the package name you pass them. In any case, anything involving a shebang has to do with your shell/kernel.
Re: Can I do single-file, Object oriented perl?
by mikeraz (Friar) on Jul 05, 2005 at 21:45 UTC

    I was just wandering around this place and read How a script becomes a module by brian_d_foy. It walks through exactly what you want to do. Start with script, change code, make code an internal to the script module ...

    Joe Bob sez check it out.

    Be Appropriate && Follow Your Curiosity
Re: Can I do single-file, Object oriented perl?
by castaway (Parson) on Jul 05, 2005 at 21:50 UTC
    Yes, you can. The easiest way, is to create your .pl file as normal, with the #! line at the top, and all the code that uses your modules. Then put your module code, ie each of the packages, at the bottom.

    If you prefer the script code at the bottom, put "package main;" after the package code, to flip back to the main package, and you don't need to end the file with 1;

    If you want more specific answers, please explain *which* errors you got while putting prints in (and give the exact code you tried, or a simplified version thereof).

    C.

Re: Can I do single-file, Object oriented perl?
by adrianh (Chancellor) on Jul 06, 2005 at 12:04 UTC
    What is the correct syntax to create tom/dick/harry objects all in a single .pl file and then use them?

    Being Perl there is no "correct" way - TIMTOWTDI and all that :-) That said I tend to always write inline classes like this:

    { package Foo; sub new { bless {}, shift } # ... other methods } { package Bar; use base 'Foo'; # Bar specific methods }

    Sticking each package in its own { block } means I don't have to worry about lexical variables leaking between packages, and using use base rather than assigning to @ISA directly means I don't have to add extra use BaseClass lines if I decide to move it to a separate file.

Re: Can I do single-file, Object oriented perl?
by mrpeabody (Friar) on Jul 06, 2005 at 17:27 UTC
    $ echo 'package Tom;' > foo.pl $ chmod u+x foo.pl $ ./foo.pl package: not found
    This is where splain comes in handy.

    $ ./foo.pl 2>&1 | splain package: not found (#1) (A) You've accidentally run your script through the Bourne shell instead of Perl. Check the #! line, or manually feed your script into Perl yourself.

    Your mistake in posting was to say "my code looks something like this, it gives some errors" and then go into a long tangent about what you THINK might be causing the errors. Much better is to say "here's the exact code, here is what I expected it to do, and here is the exact error I got." Smart Questions HOWTO.

    (The mystery of how somebody who doesn't understand that #! is parsed by the shell got a job adminning 800 Linux boxes is left as an exercise to the reader.)

      The mystery of how somebody who doesn't understand that #! is parsed by the shell got a job adminning 800 Linux boxes is left as an exercise to the reader.

      What mystery? That's the norm, in my experience.

Re: Can I do single-file, Object oriented perl?
by jdporter (Paladin) on Sep 21, 2005 at 22:57 UTC