tags:

views:

91

answers:

5

I have a rather complex data structure I've implemented in Perl. This has been broken up into about 20 classes. Basically, any time you want to use one of these classes, you need to use all of them.

Right now, if someone wants to use this data structure, they need to do something like:

use Component::Root;
use Component::Foo;
use Component::Bar;
use Component::Baz;
use Component::Flib;
use Component::Zen;
use Component::Zen::Foo;
use Component::Zen::Bar;
use Component::Zen::Baz;
... # 15 more of these...
use Component::Last;

to be able to manipulate all parts of it. How can I write a module that does this for the user, so all they have to do is

use Component;

to get all of the other modules imported?

In this particular case, the modules are all classes and don't have exports.

A: 

Moose::Exporter seems to do this, although all your other modules will have to use it as well.

In Component:

Moose::Exporter->setup_import_methods(
    also => [qw/Component::Root Component::..*/],
);
Pedro Silva
+1  A: 
  • You could use the export_to_level method for all those packages that are Exporters.

    MyPackage->export_to_level($where_to_export, $package, @what_to_export);
    
  • You could also just export all the symbols you import.

    use PackageA qw<Huey Dewey Louie>;
    ...
    our @ISA = qw<Exporter>; #inherit Exporter
    our @EXPORT = qw<Huey Dewey Louie>;
    
  • However, if you don't want to export any symbols, and just want to load modules, then just include those use statements above, and any package in the process will be able to instantiate them as classes, say if they were all OO modules.

    Provided that they have been loaded successfully, they will exist in %INC and the symbol table.

Axeman
These are good ideas if you need to export something, but in this case there's nothing to export.
cjm
+6  A: 

If these are just classes (i.e. they don't export any functions or variables when you use them), then all that really matters is that they have been loaded.

Just create Component.pm:

package Component;

our $VERSION = '1.00';

use Component::Root;
use Component::Foo;
use Component::Bar;
use Component::Baz;
use Component::Flib;
use Component::Zen;
use Component::Zen::Foo;
use Component::Zen::Bar;
use Component::Zen::Baz;
... # 15 more of these...
use Component::Last;

1; # Package return value

You don't need Exporter or anything like it.

However, instead of having a module that is nothing but use statements, it probably makes more sense to put those use statements into the class of the root node, or into the module that creates the data structure. That is, people will want to say:

use Component::Root;
my $root = Component::Root->new(...);

or

use Component qw(build_structure);
my $root = build_structure(...);

depending on how your data structure is normally created. It might be a bit confusing for people to write:

use Component;
my $root = Component::Root->new(...);

but it really depends on what your API looks like. If there are a number of classes that people might be calling new on, then use Component might be the way to go.

cjm
Your second thought is exactly right: users will be creating new instances of most of the Component::* objects at one point or another, at least, initially or if they want to do things by hand.
Robert P
You second thought is important to create good objects. By including all the required support modules in the root node of the object the object self-contained. This is an important part of encapsulation.If I have to invoke two modules, the one with new and some other module to bring in all my references then the object isn't self-contained.
HerbN
`1)` I can't believe this was a real question. I'm tempted to think this was created just for points. -- Of course you just package it all together. -- `2)` Regarding the second, it depends on how many classes might need to load this, if it's just one, load all them in the base class. If it's more than one that needs to use this, then package them all in one module and call that module from each of the other packages.
vol7ron
@vol7ron: Sorry you feel that way, but you're wrong about #1. The 'best' way to do things in Perl is quite often not the most obvious way. It's better to ask and get consensus than to blindly stumble around.
Robert P
**@Robert P:** Yes, a great reply to an honest remark. I would never outright chastise someone truly asking something they don't know; however, with your seemingly high level of understanding, one would think that you wouldn't ask a question so basic.
vol7ron
A: 

If the modules do not export anything and don't have an import method (same requirements as cjm's answer) you just need to load the modules without import:

package Component;

our $VERSION = '1.00';

require Component::Root;
require Component::Foo;
require Component::Bar;
require Component::Baz;
require Component::Flib;
require Component::Zen;
require Component::Zen::Foo;
require Component::Zen::Bar;
require Component::Zen::Baz;
... # 15 more of these...
require Component::Last;

1; # Package return value

The users of the module will just do:

require Component;

If however some modules do exports, you will have to call their import method. So you have add an import method in your Component module that will call them:

sub import
{
    Component::Root->import;
    Component::Foo->import;
    ...
}

and so the module users will have to use it:

use Component;

Note that you may have to use some other tricks if the imported module has to insert symbols in the importer's context. See for example how the POE's import does it.

dolmen
A: 

The Modern::Perl module touts itself with “enable all of the features of Modern Perl with one command,” where that command is

use Modern::Perl;

and those features are

For now, this only enables the strict and warnings pragmas, as well as all of the features available in Perl 5.10. It also enables C3 method resolution order; see perldoc mro for an explanation.

That's a lot for one line of code, which according to the perlmod documentation is exactly equivalent to

BEGIN { require Module; import Module; }

Consider Modern::Perl's implementation:

package Modern::Perl;

our $VERSION = '1.03';

use 5.010_000;

use strict;
use warnings;

use mro     ();
use feature ();

sub import {
    warnings->import();
    strict->import();
    feature->import( ':5.10' );
    mro::set_mro( scalar caller(), 'c3' );
}

1; # End of Modern::Perl

To adapt this to your situation, from your top-level module use all the other modules you want to load, and call their imports, if any, from MyTopLevelModule::import.

Note that you don't necessarily need to copy

use 5.010_000;

into MyTopLevelModule.pm, but that would be a fine idea! According to the use documentation:

In the peculiar use VERSION form, VERSION may be either a positive decimal fraction such as 5.006, which will be compared to $], or a v-string of the form v5.6.1, which will be compared to $^V (aka $PERL_VERSION). An exception is raised if VERSION is greater than the version of the current Perl interpreter; Perl will not attempt to parse the rest of the file. Compare with require, which can do a similar check at run time.

Greg Bacon