2009-02-12 5 views
4

Je vais commencer par dire que je ne suis pas du tout expérimenté dans la création de modules Perl donc je suis désolé si je suis loin d'ici.Comment puis-je inclure tout/partie des "sous-modules" dans un script Perl?

Disons que je suis en train de créer quelques modules:

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

Comme je ne sais pas ce qu'ils sont appelés, je fais appel aux modules de a.pm et b.pm « sous-modules » car ils sont lié au module bar.pm, mais pourrait encore être quelque peu indépendant. Donc, un de mes scripts Perl pourrait utiliser foo :: bar :: a, un autre script pourrait utiliser foo :: bar :: b, et peut-être que j'ai un autre script qui a besoin d'utiliser les fonctions de "a" et " b ". Au lieu de dire ceci:

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

Je veux faire quelque chose comme ceci:

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

Dans mon esprit, qui donne mon accès au script à tout bar.pm, a.pm, et b .pm.

J'ai testé quelque chose comme ça, et je me trompais manifestement.

Est-ce quelque chose comme cela possible? Je suppose que je pourrais avoir bar.pm utiliser a.pm et b.pm, et ensuite avoir des fonctions "wrapper" qui passent l'appel sur les "sous-modules", mais il semble qu'il y aurait un moyen plus facile.

Répondre

5

Regardez mon module Test::Data pour un exemple à faire cela. Même si vous pouvez y arriver, je n'ai jamais été très friands du résultat. Vous pourriez envisager une approche Plugin ou Mixin à la place. Il y a quelques modules sur CPAN qui peuvent aider avec ceci.

Voici la coutume import que j'ai écrit pour le 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([email protected]) 
      { 
      carp "Could not require Test::Data::$package: [email protected]"; 
      } 

     $full_package->export($caller); 
     } 

    } 
+0

Exactement! Voici la première ligne de votre module Test :: Data: "Le module Test :: Data emporte simplement les fonctions des modules Test :: Data :: * ." C'est exactement ce que je cherchais - seulement ce n'est pas aussi simple que je le pensais. Merci pour la solution! – BrianH

+0

P.S. Il semble qu'il y ait un type dans votre documentation de Test :: Data. les "emports" devraient probablement être des "importations". Pas que je m'en soucie - juste pensé que je l'amènerais au cas où vous ne l'auriez pas remarqué ... – BrianH

+0

Et puis j'ai eu une faute de frappe ... "tapez" dans mon commentaire précédent devrait être "faute de frappe". Ooof! – BrianH

2

Oui, vous pouvez le faire. Cela impliquera probablement d'écrire un 'sub import' personnalisé dans foo :: bar qui interprète les arguments entrants comme vous le souhaitez.

Vous utilisez probablement Exporter pour l'instant, et c'est le manque de prise en charge de votre syntaxe qui pose problème. Vous constaterez qu'il n'y a rien de particulièrement spécial à propos de la syntaxe du module Exporter implémente; c'est juste une convention commune. Cependant, vous voudrez probablement voir comment cela fonctionne pour avoir un aperçu de ce que vous voulez faire.

+0

Oui, j'utilise Exporter. Et bon point - je suis sûr que ma syntaxe n'était pas correcte - je ne voulais pas blâmer le module Exportateur. – BrianH

+0

Je ne veux pas dire que vous faites quelque chose de mal. Ce que je veux dire, c'est que ce que vous faites est (probablement) en dehors de la portée/mission de l'exportateur et appelle à votre propre mise en œuvre. (Bien que vous devriez probablement lire attentivement la documentation de l'exportateur, peut-être qu'il peut être enseigné à le faire.) – chaos

+0

Oh, pas de problème. J'ai regardé à travers le document de l'exportateur, et j'ai trouvé EXPORT_TAGS qui est ce que je pensais que ce serait - mais je ne pouvais pas obtenir que cela fonctionne correctement – BrianH

0

Oui, mais vous devez rig votre importation sous:

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; 
1

Si vous ne savez pas quoi un module est appelé, pourquoi l'incluez-vous? Vous ne devriez pas avoir besoin de l'inclure. Incluez seulement un module dans le module (appelant) qui en a besoin, et nulle part ailleurs.

En d'autres termes: si vous l'utilisez, utilisez-le. Si vous ne l'utilisez pas, ne l'utilisez pas.

+0

Je sais ce que le module est appelé. Je veux l'utiliser comme Java, où vous pouvez importer com.sun. * Au lieu de com.sun.string, com.sun.number, com.sun.whatever ... – BrianH

+0

Brian - ce n'est pas une bonne approche, pour 2 raisons (au moins, si votre code fonctionnera dans un environnement de production): 1. y compris le code que vous n'utilisez pas - qui doit être recompilé chaque fois que vous exécutez votre script - le rend plus lent qu'il peut 2. Les dépendances supplémentaires sur le code qui ne sont pas nécessaires compliquent la gestion des modifications et nécessitent des tests inutiles – DVK

0

J'ai cherché la solution similaire à la récente.Je sais - trop vieux fil mais je voudrais commenter la réponse (fév 12 '09 à 17:55) par brian d foy mais malheureusement je n'ai pas assez de réputation pour accomplir ceci. C'est pourquoi j'ajoute mon commentaire en tant que nouvelle réponse.

Sa réponse m'a aidé à résoudre le problème similaire au récent. Mais il nécessite quelques modifications si est utilisé avec use lib.

J'ai un tas de modules qui ressemblent à A::B::*. Ceux-ci doivent être chargés dans les scripts par le module général A::B. Tous ces modules sont dans leurs fichiers sous le même répertoire que le script de chargement. En utilisant le mécanisme suggéré par brian d foy nous pouvons obtenir beaucoup erreurs de sous-programme redéfinies. Pour éviter tous, je crois, j'ai trouvé une meilleure solution, mieux que no warnings 'redefine'. Maintenant, nous sommes libres d'utiliser use lib, no warnings 'redefine' ou shift @INC, ... dans le script principal.

 

    sub import { 
     @TAGS = (@_); 
     my $me = shift @TAGS; 

     (my $pm = $me) =~ s|::|/|g; 
     $pm .= ".pm"; 

     ($dir = $INC{$pm}) =~ s/\.pm$//; 
     foreach (glob "$dir/*.pm") { 
      /(\w+)\.pm$/; 
      my $module = "${me}::$1"; 

      eval "use $module qw(:all)"; # You are free to use any items in the exporting list 
      die "$me: Error while loading $module from $_: [email protected]\n" if [email protected]; 
     } 

     # Fill in @EXPORT_OK and %EXPORT_TAGS manually from each A::B::*::EXPORT_OK 
     # ... 

     goto &{ Exporter->can("import") }; 
    } 

Questions connexes