Почему мы используем объект контекста Catalyst?Какова его цель? - PullRequest
5 голосов
/ 19 июня 2011

Я думал, что не очень понимаю, почему практически все в катализаторе использует объект контекста.Кажется, что почти все начинается с

my ( $self, $c ) = @_;

, мы оборачиваем DBIC моделью катализатора и в итоге получаем

$c->model('DBIC::Table') ...

или, может быть,

$c->log->warn('foo');

, ноЯ не понимаю, почему мы просто не делаем

log('warn', 'foo'); # or whatever the API for some log library is.

Почему мы делаем все, хотя объект контекста?что делает его особенным?

Ответы [ 3 ]

5 голосов
/ 19 июня 2011

Если я правильно понимаю, что происходит (и я не очень внимательно смотрел на катализатор, так что это легко возможно), переменная контекста является вызывающей структурой.Когда поступает запрос, фреймворк встраивает всю информацию в себя и вызывает метод в вашем классе, передавая себя таким образом, чтобы у вашего метода был доступ ко всей этой информации и остальной части фреймворка.Вы можете обнаружить, что чтение о инверсии управления (или IoC) помогает вам понять.

Кроме того, оборачивая все функции в контекстную переменную, вы не сталкиваетесь с какой-либопроблемы с пространством имен.Классы контроллера, модели и т. Д. Должны иметь только методы, которые они объявляют в своем пространстве имен.

2 голосов
/ 19 июня 2011

Распространенная идиома в Perl и других языках - обойти "божественные" объекты, которые эффективно обеспечивают интерфейс к пространству имен.Это позволяет вызывать методы вместо того, чтобы требовать импорта всех функций в ваше пространство имен.Каждое из этих пространств имен также может быть настроено на разные данные времени выполнения (экземпляры объекта).

Если вам не нравится синтаксис вызова методов для объекта, звучит так, будто вы ищете что-топохож на блок with в Javascript.Хотя Perl не имеет встроенной структуры, которая делает это, он предоставляет инструменты для ее создания:

use warnings;
use strict;
use Carp ();

sub with ($&) {
    my ($obj, $code) = @_;
    my $auto = (caller).'::AUTOLOAD';
    no strict 'refs';
    local *$auto = sub {
        my ($name) = $$auto =~ /([^:]+)$/;
        my $method = $obj->can($name)
                  || $obj->can(lcfirst $name)
            or Carp::croak "no method '$name' on '$obj' in with block";
        unshift @_, $obj;
        goto &$method
    };
    $code->()
}

Учитывая макет объекта:

{package Obj;
    sub new   {bless []}
    sub log   {shift; say "logging @_"}
    sub model {shift; say "setting model to @_"}
}

Затем вы можете написать:

my $c = Obj->new;

with $c => sub {
    model('DBIC::Table');
    Log('hello world');   # ucfirst
    &log('hello again');  # or with a & since 'log' is a builtin
};

Что печатает:

setting model to DBIC::Table
logging hello world
logging hello again

Получайте удовольствие, просто имейте в виду, что встроенные имена или имена уже определенных подпрограмм не будут переопределены в блоке with.Вы можете использовать ucfirst версию имени или просто вызвать метод в этих случаях.Все новые подпрограммы в блоке with также должны вызываться с парэнсами Log('hello'), а не Log 'hello', так как имя не известно во время компиляции.

1 голос
/ 20 июня 2011

Кто-то, как правило, пишет все, что вы показали в Catalyst :: Controller.Теперь вы должны помнить, что существует контроллер Catalyst для сопоставления ваших URL.Конечно, можно импортировать множество функций в контроллер, но когда Catalyst сам импортирует функцию log, как вы используете эту функцию для сопоставления URL-адресов?

Например, sub log : Local { ... }.В скором времени это будет невозможно, или это будет сложнее, чем должно быть.Контроллер почти не имеет функций, поэтому вам не нужно запоминать много функций и не возникает никаких конфликтов.

По той же причине, по которой сам Perl решил использовать специальные символы в специальных переменных.Как $/, $_, $] и так далее.Конечно, они могут также использовать $INPUT_RECORD_SEPARATOR или $RS в качестве значения по умолчанию, но тогда вам нужно знать их, и это, вероятно, может конфликтовать с вашим кодом, если вы не знаете всех специальных переменных.

Другая причинав том, что ваши дополнительные функции, которые вы вызываете на $c, имеют некоторый контекст.Например, вы можете включить или отключить ведение журнала с помощью $c->log->disable('warn', 'error') или просто включить их.Этот контекст правильно передается в более глубокий контроллер.И они не являются глобальными, вы можете установить их при каждом запросе в другое состояние.

Другая причина заключается в том, что используемая вами дополнительная функциональность может, а иногда и необходима для чтения файла конфигурации или других вещей.Использование объекта, который вы передаете для каждого запроса (каждый $c является особенным для каждого запроса) и может изменяться для каждого запроса, дает вашему расширению возможность запрашивать информацию из вашего приложения или обрабатывать состояние для определенного запроса.

Но если вы все еще не хотите этого, вы не обязаны использовать $c.Например, вы можете просто загрузить Log :: Log4Perl вручную и использовать для этого специальный конфиг, а вовсе не использовать $c->log.Или вы можете импортировать много функций самостоятельно.Но я думаю, что по умолчанию не загрязнять пространство имен и давать вам возможность делать что-то особенное для каждого запроса, это хороший вариант по умолчанию.

И, по крайней мере, не существует правила, которое вы должны использовать $c.Например, я сам использую DateTime напрямую и создаю новые объекты и не использую Catalyst :: Plugin :: DateTime, который позволяет мне делать $c->datetime.И я не вижу никакой пользы от последнего.То, что существует множество плагинов, расширяющих $c, не означает, что они полезны, или вы должны их использовать.

...