2015-08-28 3 views
1

Модификация массива @INC для Perl кажется для отдельной области очень запутанной. Я хотел бы получить некоторое разъяснение, поскольку он, кажется, сражается с любыми средствами динамической инициализации объектов.perl: изменить @INC только для текущей области

Можно было бы подумать, что я мог бы определить его как локальный, чтобы решить эту проблему.

В соответствии с руководством, «local изменяет перечисленные переменные локально на прилагаемый блок, файл или eval».

Часть, которая раздражает меня, является частью «или».

Проблема: Я хотел бы изменить массив @INC, чтобы включить один и ТОЛЬКО один каталог при определенных обстоятельствах и ТОЛЬКО для текущего файла.

Пример попытки и вопросы:

Допустим, у меня есть index.pl запуск сценария:

#!/usr/bin/perl 
use strict; 
use warnings FATAL => 'all'; 

use File::Basename; 
# Lets say I want to modify @INC here to look in ONLY one path. Local 
# should allow us to declare for one scope or file (how non explicit this 
# is annoys me) Since I have not defined a scope with brackets, it should 
# be effective for the current file 

local @INC = (dirname(__FILE__) . '/foo/'); #some relative path 

# Lets say bar now uses standard perl modules 
require 'bar.pm'; 
# ^- This will fail because local did not work as described, fails at use 
# XML::Simple because it is traversing foo 

my $bar = bar->new(); 

Ради того, чтобы быть всеобъемлющей, вот bar.pm:

package bar; 
use strict; 
use warnings; 

sub new 
{ 
    my $class = shift; 
    my $self = bless {}, $class; 

    use XML::Simple; 
    return $self; 
} 
1; 

Есть ли способ изменить @INC ТОЛЬКО для текущего файла, оставив его неповрежденным во всех проанализированных файлах позже?

(я знаю, что могу unshift, но в конце концов, там может быть десятки каталогов это может быть пересекающие)

ответ

2
require(dirname(__FILE__) . '/foo/bar.pm'); 
+0

Я собирался downvote это, как не решает актуальную проблему изменения @INC, но это действительно работает на ~ 90% того, что им пытаются сделать , – steve

+1

Вы попросили изменить '@ INC' в середине' require'. Это довольно сложно сделать без изменения 'require'. Неважно, заметьте. Замена '@ INC' магической переменной будет работать. – ikegami

0

local @INC работы, но ваш файл bar.pm все еще должен быть в состоянии найти XML/Simple.pmuse заявление выполняется в момент компиляции файла, независимо от того, где он появляется в файле), предположительно от оригинала @INC, поэтому ваш местный @INC должен начинаться с копии оригинала @INC.

{ 
    local @INC = (dirname(__FILE__) . '/foo/', @INC); 

    require 'bar.pm'; 
}  # local @INC out of scope now, original @INC restored 

my $bar = bar->new(); 
+1

Нет, '@ INC' все еще изменяется, когда компилируется' bar.pm'. Чтобы ваш код работал, ему пришлось бы изменить 'use XML :: Simple ', чтобы' require XML :: Simple''. Мессинг с '@ INC' и искажение модуля не так хорошо, как передача правильного имени файла' require'. – ikegami

+0

Как это отличается от простого переключения на @INC, не объявляя его локальным? По существу, вы создаете копию в текущей области, которая продолжается до тех пор, пока она не покинет текущую область. Это было бы излишним. – steve

+0

@steve. Цель «local» - сделать резервную копию переменной и восстановить ее при выходе из области. '{local @INC = @INC; ...} 'такое же, как' {my @backup = @INC; ... @INC = @backup} ', за исключением того, что он работает, если область видимости тоже исключена. – ikegami

2
use File::Basename; 
use subs 'require'; 
sub require { 
    my $module_file = shift; 
    die "unexpected absolute path $module_file\n" if $module_file =~ /^\//; 
    CORE::require(dirname(__FILE__) . '/foo/' . $module_file); 
} 

См http://perldoc.perl.org/CORE.html#OVERRIDING-CORE-FUNCTIONS

+0

Умный - это вызов 'main :: require' из основного файла. В другом пакете внутри 'bar.pm' он вызывает встроенный' require'. Вы можете еще больше ограничить его область помощью '{local * require = sub {...} ...}' – mob

Смежные вопросы