views:

148

answers:

5

I'll just start out by saying I am not at all experienced with creating Perl modules so I'm sorry if I'm way off here.

Let's say I am creating a few modules:

foo::bar
foo::bar::a
foo::bar::b

Since I don't know what they are called, I am calling the a.pm and b.pm modules "sub modules" since they are related to the bar.pm module, but could still be somewhat independent.

So one of my Perl scripts could use foo::bar::a, another script could use foo::bar::b, and maybe I have another script that needs to use functions from both "a" and "b". Instead of saying this:

use foo::bar;
use foo::bar::a qw(one two);
use foo::bar::b;

I want to do something like this:

use foo::bar qw(:a :b);

In my mind, that would give my script access to everything in bar.pm, a.pm, and b.pm.

I tested something like this, and I was obviously wrong.

Is something like this possible? I suppose I could have bar.pm use a.pm and b.pm, and then have "wrapper" functions that pass the call onto the "sub modules" but it seems like there would be an easier way.

Thanks in advance!

Brian

+2  A: 

Yes, you can do that. It will probably involve writing a custom 'sub import' in foo::bar that interprets incoming arguments the way you want.

Probably you're using Exporter right now, and it's its lack of support for your syntax that's the issue. You'll find that there's nothing particularly special about the module syntax Exporter implements; it's just a common convention. You'll likely want to look at how it does business to get insight into how you'll want to, though.

chaos
Yes, I am using Exporter. And good point - I am sure my syntax was not correct - I didn't mean to place blame on the Exporter module.
BrianH
I don't mean to say that you're doing something wrong. What I mean is that what you're doing is (probably) outside of Exporter's scope/mission and calls for your own implementation. (Though you should probably thoroughly read Exporter's documentation, too; maybe it can be taught to do this.)
chaos
Oh, no problem. I've looked through the Exporter's doc, and I found EXPORT_TAGS which is what I thought it would be - but I couldn't get that to work correctly
BrianH
A: 

Also try looking at http://search.cpan.org/~evo/Class-MixinFactory-0.92/MixinFactory.pm

Chris J
+4  A: 

Look at my Test::Data module for an example about doing that. Even though you can make it happen, I've never been terribly fond of the result. You might want to consider a Plugin or Mixin approach instead. There are some modules on CPAN that can help with this.

Here's the custom import that I wrote for Test::Data:

sub import 
    {
    my $self   = shift;
    my $caller = caller;

    foreach my $package ( @_ )
     {
     my $full_package = "Test::Data::$package";
     eval "require $full_package; 1";
     if( $@ )
      {
      carp "Could not require Test::Data::$package: $@";
      }

     $full_package->export($caller);
     }

    }
brian d foy
Exactly! Here's the first line from your Test::Data module: "The Test::Data module simply emports functions from Test::Data::*modules." That is exactly what I was looking for - only it isn't as straight forward as I thought. Thanks for the solution though!
BrianH
P.S. It looks like there is a type in your documentation of Test::Data. "emports" should probably be "imports". Not that I care - just thought I'd bring it up in case you hadn't noticed it...
BrianH
And then I had a typo... "type" in my previous comment should be "typo". Ooof!
BrianH
What's the reason for the double eval? A single stringy eval should do the job AFAIK.
Leon Timmermans
Um, double eval probably means I wrote that at 3am.
brian d foy
A: 

Yes, but you have to rig your own import sub:

use strict;
use warnings;

package ab;
use base qw<Exporter>;
our @EXPORT_OK;
our %EXPORT_TAGS;
BEGIN { 
    @EXPORT_OK   = qw<>;
    %EXPORT_TAGS = ( a => 1, b => 1, all => \@EXPORT_OK );
}

sub setup_part { 
    #use Smart::Comments;
    my $code = shift;
    my $mini_path = "foo/bar/$code.pm";
    return if exists $INC{$mini_path};
    require $mini_path; 
    my $arr_ref 
        = do { no strict 'refs';
            \@{Symbol::qualify( 'EXPORT_OK', $code )};
        };
    $code->import( @$arr_ref );
    push @EXPORT_OK, @$arr_ref;
    $EXPORT_TAGS{$code} = [ @$arr_ref ];
    return;
}

sub import { 
    my ( $package_name, @imports ) = @_;
    my %import_hash = map { $_ => 1 } @imports;
    if ( exists $import_hash{':all'} ) { 
        @import_hash{qw<:a :b>} = ( 1, 1 );
    }
    foreach my $import ( grep { exists $import_hash{$_} } qw<:a :b> ) { 
        setup_part( substr( $import, 1 ));
    }
    goto &{Exporter->can( 'import' )};
}

1;
Axeman
+1  A: 

If you don't know what a module is called, why are you including it? You shouldn't need to include it. Only include a module in the (calling) module that needs it, and nowhere else.

That is: if you are using it, then "use" it. If you don't use it, don't "use" it.

Ether
I know what the module is called. I want to use it like java, where you can do import com.sun.* instead of com.sun.string, com.sun.number, com.sun.whatever...
BrianH
Brian - this is not a good approach, for 2 reasons (at least, if your code will run in a production environment):1. including code you don't use - which has to be re-compiled every time you run your script - makes it slower that it can be2. Extra dependencies on code that is not needed complicates change management and necessitates un-needed testing
DVK