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

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

#!/usr/bin/perl

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

# Potentially many more.

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


person cowgod    schedule 13.01.2009    source источник


Ответы (6)


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

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';
person jrockway    schedule 13.01.2009
comment
Это не позволит экспортировать функции, содержащие слово BEGIN или import (например, функцию с именем do_important_work()) — измените регулярное выражение на /\A(?:BEGIN|import)\z/, чтобы исправить это. Также требуется истинное выражение (например, 1;) в качестве последнего утверждения. - person j_random_hacker; 03.02.2009
comment
Более тонко, он попирает $f, @f, %f и т. д. при экспорте функции f(). Я не уверен, как это можно исправить. - person j_random_hacker; 03.02.2009
comment
Добавляя {CODE} после второго глобуса. - person jrockway; 04.02.2009
comment
@jrockway: это при условии, что пакет хотел экспортировать функцию f(). Что, если вместо этого они хотят экспортировать скаляр $f, массив @f или хэш %f? Невозможно сказать. - person j_random_hacker; 05.02.2009

Да, можно, но нет, не стоит этого делать.

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

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

person innaM    schedule 13.01.2009
comment
Если объединенные вместе модули не имеют ничего общего, я полностью согласен с этим, но группировка связных, связанных модулей на самом деле полезна, так же как хорошо сгруппировать много связных строк кода в функцию в одном месте, а затем вызвать это много раз. - person j_random_hacker; 14.01.2009
comment
Я полностью не согласен. Прости. Что, если бы все модули делали одно и то же с небольшими вариациями? Зачем использовать их все время, а не только тот, который вам нужен прямо сейчас? Для группировки вещей, которые связаны друг с другом, у нас есть пространства имен пакетов в Perl. - person innaM; 14.01.2009
comment
Если все модули в основном делают одно и то же, конечно, имеет смысл использовать только один, а не все сразу. Но если вы имеете дело с набором дополнительных модулей, почему бы не сгруппировать их? Единственный недостаток, который я вижу, это возможные проблемы с производительностью, если вы позволите группам стать огромными. - person j_random_hacker; 15.01.2009
comment
Я не согласен. Многие другие языки делают это — см. ASDF в CL. Единственный способ использовать модуль — использовать его везде. - person jrockway; 02.02.2009
comment
Единственный способ использовать модуль — использовать его везде? - Извините, но я понятия не имею, что это значит. - person innaM; 02.02.2009

да.

В 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), это кажется излишним с моей точки зрения.

person Jonathan Leffler    schedule 13.01.2009
comment
В моем примере предполагается, что вам нужны экспортированные символы из каждого модуля. Path::Class и Carp являются примерами модулей, которые экспортируют символы. Когда вы используете Lots::Of::Modules, вы получаете экспорт всего, что вы использовали при определении Lots::Of::Modules. - person jrockway; 13.01.2009

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

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

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

В Lots/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; вызывается более одного раза, например, в отдельных модулях used основной программой). Более примитивный do не генерирует исключение, если ему не удается найти файл, названный его аргументом в @INC, поэтому необходимо проверить результат с помощью defined.

person j_random_hacker    schedule 14.01.2009
comment
Это не работает, если вы полагаетесь на то, что функции будут доступны во время компиляции, поскольку do запускается во время выполнения. Побочным эффектом является то, что прототипы импортированных функций не будут работать. - person jrockway; 02.02.2009
comment
Хорошая мысль, джроквей. Я добавил блок BEGIN, чтобы исправить это. - person j_random_hacker; 03.02.2009
comment
Это также имеет недостаток в том, что он полагается на жестко запрограммированный путь, а не на поиск @INC, как это делает «use». Ничего страшного для локальной разработки, но получайте удовольствие, когда пытаетесь установить его в другом месте... - person Dave Sherohman; 03.02.2009
comment
@ Дэйв Шерохман: я согласен, что это проблема. Основная проблема заключается в том, что use — это почти именно то, что нам нужно — если бы был какой-то способ заставить его всегда включать файл, этого уродливого обходного пути можно было бы избежать. (Может быть, BEGIN { local %INC; use Lots::Of::Modules; }?) - person j_random_hacker; 03.02.2009
comment
использование = требование + импорт. require запускается один раз, import запускается каждый раз. - person jrockway; 03.02.2009

Другим вариантом было бы для 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;

(Инструкции «use», вероятно, могут выходить за пределы BEGIN, но именно там они были в коде, который я проверил, чтобы убедиться, что это работает так, как я думал. Этот код на самом деле evals uses, поэтому у них есть причина для быть внутри BEGIN, что, вероятно, не применимо в вашем случае.)

person Dave Sherohman    schedule 03.02.2009

используя @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()
person Sérgio    schedule 14.11.2013