From: Mark Mackey on
Hi all.

I've got a simple problem that I can't work out an elegant way to solve.
I have a simple Perl module which is basically a wrapper for some
external binaries. I want the module to check for the existence of those
binaries on import, but only for the binaries which are actually going
to be used by the subroutines that are exported.

For example, I have Thingy.pm:

package Thingy;

require Exporter;
@ISA = qw(Exporter);
# symbols to export on request
@EXPORT_OK = qw(sub1 sub2 sub3);

use strict;

sub sub1 {
print `/bin/ls`;
}

sub sub2 {
print `/bin/true`;
}

sub sub3 {
print `/bin/no_exist`;
}

BEGIN {
my(@required)=qw( /bin/ls /bin/true /bin/no_exist );
my(%required)=( sub1=>"/bin/ls", sub2=>"/bin/true", sub3=>"/bin/no_exist" );
foreach (@required) {
print "Checking $_\n";
die "Can't find required utility $_" if (! -x $_);
}
}
1;

Sub1 will only work if "/bin/ls" is present, sub2 will only work if
"/bin/true" is present, and sub3 will only work if "/bin/no_exist" is
present. With the current structure, all 3 are checked on import, so

use Thingy qw();

dies telling me that "bin/no_exist" doesn't exist :).

What I want is to be able to check the relevant binaries' existence only
for the methods that are imported, so in this case you could do

use Thingy qw(sub1);

and everything would be happy, but

use Thingy qw(sub1 sub2 sub3);

would bail out and inform me that 'sub3' depends on "/bin/no_exist" and
hence it can't continue.

The reason I'd like this check is that this module is used in lots of
programs, each of which uses a different subset of its subroutines. Some
of the programs have very long runtimes, so I don't really want them to
run for hours and then suddenly bail out when sub3() finally gets
called. I suspect the answer involves fiddling around with import()
methods, but I don't completely understand the documentation for
Exporter.pm about this.

Help?

--
Mark Mackey http://www.swallowtail.org/
code code code code code code code code code code code code code bug code co
de code code code bug code code code code code code code code code code code
code code code code code code code code code code code code code code code c



From: Uri Guttman on
>>>>> "MM" == Mark Mackey <markm(a)chiark.greenend.org.uk> writes:


MM> require Exporter;
MM> @ISA = qw(Exporter);

those two lines are better done as:

use base 'Exporter'

MM> # symbols to export on request
MM> @EXPORT_OK = qw(sub1 sub2 sub3);

MM> use strict;

put the strict line before all the others. then use our before
@EXPORT_OK. the use base line will still work correctly too.

MM> What I want is to be able to check the relevant binaries' existence only
MM> for the methods that are imported, so in this case you could do

MM> The reason I'd like this check is that this module is used in lots of
MM> programs, each of which uses a different subset of its subroutines. Some
MM> of the programs have very long runtimes, so I don't really want them to
MM> run for hours and then suddenly bail out when sub3() finally gets
MM> called. I suspect the answer involves fiddling around with import()
MM> methods, but I don't completely understand the documentation for
MM> Exporter.pm about this.

you just need to declare your own import() method. it is called with the
class name and the arguments passed on the use line so you will get
'sub1', etc. do your checks then and die or whatever as needed. if all
the requested features are supported, then you can pass the import
request on to Exporter with: (untested)

sub import {

my( $class, @features ) = @_ ;

# do your testing of the requested features in @features
# and die as desired.

# at this point everything wanted is found

$class->SUPER::import( @features ) ;
}

since you are subclassing Exporter, it will get the same call as if it
was handled directly without your intervening import method.

uri

--
Uri Guttman ------ uri(a)stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
From: Paul Lalli on
Mark Mackey wrote:

> I've got a simple problem that I can't work out an elegant way to solve.
> I have a simple Perl module which is basically a wrapper for some
> external binaries. I want the module to check for the existence of those
> binaries on import, but only for the binaries which are actually going
> to be used by the subroutines that are exported.
<snip>
> I suspect the answer involves fiddling around with import()
> methods,

You are correct.

> but I don't completely understand the documentation for Exporter.pm about this.

That's okay, because import() doesn't intrinsically have anything to do
with Exporter;

import() is not a built in. It is a method that you define in your
package, that is automatically called when the user calles 'use
Thingy;'. More specifically, if the user calls 'use Thingy qw/ls
true/', then the import method is called with those two parameters.

So, within your package, define your import() method. In it, retrieve
what elements are being requested, via @_, like any other method or
subroutine. Do whatever checks are necessary. And once you're done,
you can then invoke Exporter's magic by following the "Exporting
without using Exporter's import method" section of Exporter's
documentation:

use Carp;
sub import {
my $class = shift;
my @subs_to_export = @_;
my %required_for = (
sub1=>"/bin/ls",
sub2=>"/bin/true",
sub3=>"/bin/no_exist"
);
foreach (@subs_to_export){
print "Checking $_\n";
croak "Can't find required utility for $_"
unless -x $required_for{$_};
}
#we got here, all the required checks passed. Export everything
#the user wanted:
$class->export_to_level(1, $class, @required);
}

> Help?

Hope this helps.

Paul Lalli

From: Mark Mackey on
In article <1138297798.750898.238030(a)g43g2000cwa.googlegroups.com>,
Paul Lalli <mritty(a)gmail.com> wrote:
>
>import() is not a built in. It is a method that you define in your
>package, that is automatically called when the user calles 'use
>Thingy;'. More specifically, if the user calls 'use Thingy qw/ls
>true/', then the import method is called with those two parameters.

Ah, that makes sense, and I now understand what the Exporter.pm docs
were wittering about. Where is the fact that the import method is called
on 'use' documented? I'd thought that I'd read all of the relevant bits
of the man pages, but I missed that one.

Thanks!

--
Mark Mackey http://www.swallowtail.org/
code code code code code code code code code code code code code bug code co
de code code code bug code code code code code code code code code code code
code code code code code code code code code code code code code code code c
From: Paul Lalli on
Mark Mackey wrote:
> In article <1138297798.750898.238030(a)g43g2000cwa.googlegroups.com>,
> Paul Lalli <mritty(a)gmail.com> wrote:
> >
> >import() is not a built in. It is a method that you define in your
> >package, that is automatically called when the user calles 'use
> >Thingy;'. More specifically, if the user calls 'use Thingy qw/ls
> >true/', then the import method is called with those two parameters.
>
> Ah, that makes sense, and I now understand what the Exporter.pm docs
> were wittering about. Where is the fact that the import method is called
> on 'use' documented? I'd thought that I'd read all of the relevant bits
> of the man pages, but I missed that one.

perldoc -f import
import There is no builtin "import" function. It is just
an ordinary method (subroutine) defined (or
inherited) by modules that wish to export names to
another module. The "use" function calls the
"import" method for the package used.

perldoc -f use
use Module LIST
Imports some semantics into the current package from
the named module, generally by aliasing certain
subroutine or variable names into your package. It
is exactly equivalent to

BEGIN { require Module; import Module LIST; }

except that Module must be a bareword.

Paul Lalli