Реализация переключателей функций в Perl5 - PullRequest
3 голосов
/ 15 марта 2012

Я хотел бы иметь возможность создавать "призрачные" пакеты и подпрограммы.У меня есть файл конфигурации (ini) с записями вроде этого:

[features]
sys.ext.latex = off
gui.super.duper.elastic = off
user.login.rsa = on

Этот файл анализируется, и более поздние разработчики могут задавать вопросы, такие как:

if ( MyApp::Feature->enabled ( 'user.login.rsa' ) { ... }

(Вся идея основанана FeatureToggle Мартина Фаулера http://martinfowler.com/bliki/FeatureToggle.html)

Используя AUTOLOAD для перехвата вызовов в MyApp :: Feature и блок BEGIN для анализа ini-файла, мы можем предоставить этот API:

if ( MyApp::Feature->user_login_rsa ) { ... }

Вопросis: Возможно ли создать следующий API:

if ( MyApp::Feature::User::Login::RSA ) { ... }

, имеющий только MyApp :: Feature?

Нижний, верхний регистр может быть изменен в файле конфигурации, это не проблема здесь.И проясните: реализация отделена от конфигурации, MyApp :: Feature :: User :: Login :: RSA не существует и никогда не будет. Реализация этой функции лежит в MyApp :: Humans.

Я знаю, что размещение MyApp :: Feature :: Foo :: Bar предполагает наличие такого пакета, но разработчики знают соглашение, согласно которому пакет Feature управляет переключениями функций, и у них не возникнет проблем с этим.В первом примере (с использованием разрешенного ($ string) бита, слишком сложного для чтения

if ( package::package->method ( string ) )

, второй лучше:

if ( package::package->method )

третий будет еще проще:

if ( package::package::package )

Итак, можно ли имитировать AUTOLOAD на уровне пакета?

Привет, Роб.


1 Ответ

4 голосов
/ 15 марта 2012

Похоже, у вас есть список ключей из нескольких слов, которые вы хотите установить в пространство имен.

BEGIN {
    my %states = ( # the values that should be transformed
        on  => sub () {1},
        off => sub () {''},
    );
    sub install_config {
        my ($package, $config) = @_;
        for my $key (keys %$config) {
            my @parts = map ucfirst, split /\./, $key;
            my $name  = join '::' => $package, @parts;
            no strict 'refs';
            *{$name} = $states{$$config{$key}} # use a tranformed value
                    || sub () {$$config{$key}} # or the value itself
        }
    }
}

BEGIN {
    my %config = qw(
        sys.ext.latex            off
        gui.super.duper.elastic  off
        user.login.rsa           on
        some.other.config        other_value
    );
    install_config 'MyApp::Feature' => \%config;
}

say MyApp::Feature::Sys::Ext::Latex ? 'ON' : 'OFF';             # OFF
say MyApp::Feature::Gui::Super::Duper::Elastic ? 'ON' : 'OFF';  # OFF
say MyApp::Feature::User::Login::Rsa ? 'ON' : 'OFF';            # ON
say MyApp::Feature::Some::Other::Config;                        # other_value

Постоянные подпрограммы, установленные здесь, будут встроены в perl, когда это применимо.

Вы можете сделать install_config немного проще в использовании, поместив его в функцию импорта пакета:

BEGIN {$INC{'Install/Config.pm'}++} # fool require

sub Install::Config::import {shift; goto &install_config}

use Install::Config 'MyApp::Feature' => {qw(
    sys.ext.latex            off
    gui.super.duper.elastic  off
    user.login.rsa           on
    some.other.config        other_value
)};
...