Есть ли способ "использовать" один файл, который, в свою очередь, использует несколько других в Perl?

Я хотел бы создать несколько модулей, которые будут использоваться почти во всех сценариях и модулях моего проекта. Это может быть использовать d в каждом из моих скриптов:

#!/usr/bin/perl

use Foo::Bar;
use Foo::Baz;
use Foo::Qux;
use Foo::Quux;

# Potentially many more.

Можно ли переместить все эти операторы use в новый модуль Foo::Corge, а затем только use Foo::Corge в каждом из моих сценариев и модулей?

Ответ 1

Что-то вроде этого должно работать:

http://mail.pm.org/pipermail/chicago-talk/2008-March/004829.html

В принципе, создайте свой пакет с большим количеством модулей:

package Lots::Of::Modules;
use strict; # strictly optional, really

# These are the modules we want everywhere we say "use Lots::Of::Modules".
# Any exports are re-imported to the module that says "use Lots::Of::Modules"
use Carp qw/confess cluck/;
use Path::Class qw/file dir/;
...

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

    no strict;
    *{ $caller. '::'. $_ } = \*{ $class. '::'. $_ }
       for grep { !/(?:BEGIN|import)/ } keys %{ $class. '::' };
}

Затем используйте Lots:: Of:: Modules в другом месте;

use Lots::Of::Modules;
confess 'OH NOES';

Ответ 2

Да, это возможно, но нет, вы не должны этого делать.

Я просто потратил две недели, чтобы избавиться от модуля, который ничего не делал, кроме использования других модулей. Я предполагаю, что этот модуль стал простым и невинным. Но на протяжении многих лет он превращался в огромного зверя с множеством примеров использования, большинство из которых не были нужны ни для какого конкретного запуска нашего webapp. Наконец, потребовалось около 20 секунд, чтобы "использовать" этот модуль. И это поддерживало ленивое создание модулей копирования и вставки.

Так снова: вы можете сожалеть об этом шаге через пару месяцев или лет. И что вы получаете на стороне плюса? Вы сохранили набор двух строк в нескольких модулях. Большая сделка.

Ответ 3

Да.

В Foo/Corge.pm

use Foo::Bar;
use Foo::Baz;
use Foo::Qux;
use Foo::Quux;

1;   # Be successful

Все, что осталось, - это получить каталог, содержащий подкаталог Foo, добавленный в ваш путь к библиотеке (@INC). В качестве альтернативы создайте Foo.pm и используйте другие модули. Они будут находиться в подкаталоге Foo рядом с Foo.pm.

Если вы думаете об этом, все сложные модули Perl, которые используют другие модули, делают это все время. Они не обязательно находятся в одном пакете верхнего уровня (Foo в этом примере), но они используются так же обязательно.

Пока вы можете использовать Carp, и Path:: Class, и признаться, и так далее (как предлагает jrockway), это кажется излишним, когда я сижу.

Ответ 4

[EDIT: мое раннее решение с участием use Lots::Of::Modules; имело тонкую ошибку - см. нижнюю часть. Исправление делает вещи немного уродливыми, но все же работоспособными.]

[EDIT # 2: добавлен BEGIN { ... } вокруг кода, чтобы гарантировать, что любые функции определены во время компиляции. Благодаря jrockway для указания этого.]

Следующий код будет делать именно то, что делает код jrockway, только проще и понятнее:

В лотах /Of/Modules.inc:

use Carp qw/confess cluck/;
use Path::Class qw/file dir/;

0;   # Flag an error if called with "use" or "require" instead of "do"

Чтобы импортировать эти 4 функции:

BEGIN { defined( do 'Lots/Of/Modules.inc' ) or die; }

Поскольку у нас нет инструкции package Lots::Of::Modules; в начале этого файла, операторы use будут экспортироваться в пакет вызывающего абонента.

Мы должны использовать do вместо use или require, так как последний будет загружать только один файл (вызывая отказ, если use Lots::Of::Modules; вызывается более одного раза, например, в отдельных модулях use d на основная программа). Более примитивный do не генерирует исключение, если ему не удается найти файл, названный его аргументом в @INC, следовательно, необходимость проверки результата с помощью defined.

Ответ 5

Другим вариантом было бы для Foo:: Corge просто реэкспортировать любые интересующие предметы:

package Foo::Corge;

use base 'Exporter';
BEGIN {
  our @EXPORT_OK = qw( bar baz qux quux );

  use Foo::Bar qw( bar );
  use Foo::Baz qw( baz );
  use Foo::Qux qw( qux );
  use Foo::Quux qw( quux );
}
1;

(Операторы использования могут, вероятно, выходить за пределы BEGIN, но там, где они были в коде, который я проверил, чтобы убедиться, что это сработало так, как я думал. Это код на самом деле eval the use s, поэтому у него есть причина, чтобы они находились внутри BEGIN, что, вероятно, не применимо в вашем случае.)

Ответ 6

используя @EXPORT вместо @EXPORT_OK, проще

Библиотека:

package mycommon;

use strict;
use warnings;

use base 'Exporter';

our @EXPORT = qw(test);

sub test {
    print "this is a test";
}

1;

используйте его:

#!/usr/bin/perl
use strict;
use warnings;
use mycommon;

common::test()