Perl 我们为什么使用Catalyst';什么是上下文对象?它的目的是什么?
我在想,我真的不明白为什么catalyst中几乎所有的东西都使用上下文对象。似乎一切都是从Perl 我们为什么使用Catalyst';什么是上下文对象?它的目的是什么?,perl,catalyst,Perl,Catalyst,我在想,我真的不明白为什么catalyst中几乎所有的东西都使用上下文对象。似乎一切都是从 my ( $self, $c ) = @_; 我们用一个catalyst模型包装DBIC,最后得到 $c->model('DBIC::Table') ... 也许我们会 $c->log->warn('foo'); 但我不明白我们为什么不这么做 log('warn', 'foo'); # or whatever the API for some log library is. 为什
my ( $self, $c ) = @_;
我们用一个catalyst模型包装DBIC,最后得到
$c->model('DBIC::Table') ...
也许我们会
$c->log->warn('foo');
但我不明白我们为什么不这么做
log('warn', 'foo'); # or whatever the API for some log library is.
为什么我们要通过上下文对象做所有事情?它的特殊之处是什么?如果我正确理解了正在发生的事情(并且我没有仔细研究catalyst,所以这很容易实现),那么上下文变量就是调用框架。当一个请求传入时,框架将所有信息构建到自身中,并调用类中的一个方法传递自身,这样您的方法就可以访问所有信息和框架的其余部分。你可能会发现阅读(或国际奥委会)有助于你理解
此外,通过将所有功能包装在上下文变量中,您不会遇到任何名称空间问题。控制器、模型等。类只需要在名称空间中声明方法。Perl和其他语言中的一个常见习惯用法是传递“god”对象,这些对象有效地提供了名称空间的接口。这允许调用方法,而不需要将所有函数导入命名空间。还可以使用不同的运行时数据(对象的实例)自定义每个名称空间 如果您不喜欢在对象上调用方法的语法,那么您所寻找的似乎与Javascript的
with
块类似。虽然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
将模型设置为DBIC::Table
记录hello world
再次打招呼
有趣的是,请记住,内置名称或已定义子例程的名称不会在
with
块中被覆盖。您可以使用名称的ucfirst
版本,或者在这些实例中仅调用该方法。with
块中的所有新子例程也必须使用parensLog('hello')
调用,而不是Log'hello'
,因为在编译时名称未知。通常会有人编写您在Catalyst::Controller中显示的所有内容。现在,您必须记住,存在一个Catalyst控制器来进行URL映射。当然,可以将许多函数导入控制器,但当Catalyst本身导入log
函数时,如何使用此函数进行URL映射
例如子日志:本地{…}
。很快,这将是不可能的,否则它将比应该的更加复杂。控制器几乎没有函数,因此您不需要记住很多函数,也不会有任何冲突
这与Perl本身选择在特殊变量中使用特殊字符的原因相同。像$/
,$\uuu
,$]
等等。当然,它们也可以使用$INPUT\u RECORD\u SEPARATOR
或$RS
作为默认值,但是您需要了解它们,如果您不知道所有特殊变量,则可能会与代码冲突
另一个原因是,您在$c
上调用的附加功能具有一定的上下文。例如,您可以使用$c->log->disable('warn','error')
启用或禁用日志记录,或者只启用它们。此上下文被正确地传递到更深的控制器中。而且它们不是全局的,您可以在每个请求中将它们设置为另一个状态
另一个原因是,您使用的额外功能可以而且有时需要读取配置文件或其他内容。使用为每个请求传递的对象(每个$c
对于每个请求都是特殊的),并且可以为每个请求修改,这样扩展就可以从应用程序请求信息或处理特定请求的状态
但是,如果您仍然不希望这样做,则不会强制您使用$c
。例如,您可以手动加载Log::Log4Perl,并对其使用特殊配置,而不使用$c->Log
。或者你可以自己导入很多函数。但是,我认为不污染名称空间并允许您为每个请求执行特殊操作的默认设置是一个很好的默认设置
至少没有规则规定你必须使用
$c
。例如,我自己直接使用DateTime并创建新对象,而不使用Catalyst::Plugin::DateTime来执行$c->DateTime
。我看不出做最后一件事有什么好处。仅仅因为有很多插件扩展了$c
,并不意味着它们很有用或者你必须使用它们。这并不是一个关于语法的问题。。。虽然我更喜欢Rakudo调用方法的方式…@Xenotheracide=>你问catalyst为什么让你写$c->log->warn(…)
,而不是log(warn=>…)
。这是一个句法上的差异。之所以如此,是因为catalyst的作者认为这是最好的。因此,我给了您一种按照您想要的方式编写它的方法(尽管在本例中,它将是Log()->warn(…)
,因为这是catalyst的Log
方法的工作方式),您希望得到什么类型的答案?询问更多关于为什么所有事情都是通过上下文对象完成的,甚至外部内容通常都是通过它访问的。语法的确切原因比它通过上下文对象的原因更为明显。与此相反,直接使用日志api。