将 Dancer 插件移植到 Dancer2
在我的 Dancer2 网络应用程序中,我想知道哪些请求来自智能手机。为此有一个插件——但只适用于较老的 Dancer (v1) 框架。我不是专家,但即使是我也很容易将 Dancer 插件,Dancer::Plugin::MobileDevice,移植到 Dancer2!在这篇文章中,我们将探索 Dancer2 及其处理插件的方式。我们将深入框架的工作,并检查我为了将插件从 Dancer 移植到 Dancer2 而做出的主要更改。到文章结束时,你将准备好大显身手,并且你将拥有一个便携式参考,以便在移植插件时使用。
Dancer2 网络框架
Dancer2 应用程序在 Web 服务器上运行并处理来自浏览器的请求。应用程序的 Perl 代码使用 Dancer2 的领域特定语言(DSL)中的关键字来访问有关请求的信息。
试试这个:安装 Task::Dancer2。然后,将其保存为 app.psgi
use Dancer2;
get '/' => sub {
return 'Hello, ' . (query_parameters->{name} || 'world') . '!';
};
to_app;
并运行 plackup
。
在浏览器中输入 URL http://localhost:5000
,你将看到“Hello, world!”,或者访问 http://localhost:5000/?name=genius
以看到“Hello, genius!”。这里的“genius”来自 query_parameters
,这是一个 DSL 关键字,它返回 URL 中 ?
之后的价值。你可以使用这些值来构建对请求的响应。
Dancer 和 Dancer2 插件
Dancer 和 Dancer2 插件为插件的用户定义了新的 DSL 关键字。它们还安装了“钩子”,这些是 Dancer 处理请求时运行的子例程。钩子收集 DSL 关键字可以访问的信息。
例如,Dancer::Plugin::MobileDevice 中的钩子检测请求是否来自移动设备。该插件定义了 is_mobile_device
DSL 关键字,以便你的代码可以相应地作出反应。为了移植插件,我更改了关键字、钩子和测试套件的代码。
移植关键字
Dancer 插件使用 Dancer DSL 和特定于插件的 DSL 来定义 DSL 关键字。在 Dancer (v1) 中,使用 register
插件-DSL 函数创建 is_mobile_device
关键字(代码示例简化,以关注移植)
register 'is_mobile_device' => sub {
return request->user_agent =~ /$regex/ || 0;
};
register_plugin;
Dancer2 插件是 Moo 对象,新的 DSL 关键字是这些对象上的成员函数。因此,我将 is_mobile_device()
更改为成员函数
sub is_mobile_device {
my $self = shift; # get the plugin’s object instance
return ($self->dsl->request->user_agent =~ /$regex/) || 0 ;
}
plugin_keywords qw(is_mobile_device); # replaces register_plugin()
在函数体中,Dancer 插件直接访问 DSL 关键字 request
。Dancer2 插件则通过 $self->dsl->request
访问请求。
移植钩子
Dancer 插件使用 DSL 关键字 hook
添加钩子。例如,此 before_template
钩子在模板中使 is_mobile_device
可用
hook before_template => sub {
my $tokens = shift;
$tokens->{'is_mobile_device'} = is_mobile_device();
};
Dancer2 处理钩子的方式非常不同。插件实例创建时,将调用插件的 Moo 构造函数 BUILD
。在 BUILD
中,插件注册钩子。我添加了 BUILD
并调用 $self->dsl->hook
来添加钩子
sub BUILD {
my $self = shift;
$self->dsl->hook( before_template_render => sub {
my $tokens = shift;
$tokens->{is_mobile_device} = $plugin->is_mobile_device;
});
}
如果你的钩子函数太长而无法移动到 BUILD
中,你可以将其保留在原地,并使用 $self->dsl->hook( hook_name => \&sub_name );
。
移植测试
Dancer::Plugin::MobileDevice 有一个完整的测试套件。这些测试对开发者来说非常有用,因为它们允许你查看 Dancer2 移植是否与 Dancer 原始版本行为相同。也就是说,在使用这些测试来测试你的移植插件之前,你必须先移植这些测试!我们将先看看 Dancer 的方法,然后我会向你展示 Dancer2 的更改。
舞者测试定义了一个使用插件的简单Web应用。它们使用Dancer::Test中的辅助器来测试该应用。例如(简化自t/01-is-mobile-device.t
):
{ # The simple application
use Dancer;
use Dancer::Plugin::MobileDevice;
get '/' => sub { return is_mobile_device; };
}
use Dancer::Test;
$ENV{HTTP_USER_AGENT} = 'iPhone';
my $response = dancer_response GET => '/'; # dancer_response() is from Dancer::Test
is( $response->{content}, 1 );
另一方面,Dancer2使用Plack生态系统进行测试,而不是它自己的辅助器。为了在该生态系统中工作,我根据Dancer2手册的“测试”部分对上述测试进行了修改。
use Plack::Test; # Additional testing modules
use HTTP::Request::Common;
{
package TestApp; # Still a simple application, but now with a name
use Dancer2;
use Dancer2::Plugin::MobileDevice;
get '/' => sub { return is_mobile_device; };
}
my $dut = Plack::Test->create(TestApp->to_app); # a fake Web server
my $response = $dut->request(GET '/', 'User-Agent' => 'iPhone');
is( $response->content, 1 );
Dancer2的测试比Dancer的测试使用了更多的模板代码,但Dancer2的测试比Dancer的测试更加模块化和灵活。使用Plack后,您不再需要使用全局状态(%ENV
),并且可以在每个.t
文件中测试多个应用或用例。看到测试通过是一个很好的迹象,表明您的移植工作已经完成。
结论
我是一个Dancer2的新手,以前从未使用过Dancer。但我能在不到一天的时间内将Dancer::Plugin::MobileDevice移植到Dancer2——包括阅读文档和找出如何操作的时间!当您需要在Dancer2中使用Dancer函数时,查看下面的快速参考,您就可以开始行动了!
致谢
感谢Kelly Deltoro-White的见解,以及Dancer::Plugin::MobileDevice和Dancer2的作者为我们提供了一个强大的基础来构建。
有关Dancer2插件的更多信息
- 由Sawyer X撰写的“新的Dancer2插件系统”概述
- 有关详细信息,请参阅Dancer2::Plugin
快速参考:从Dancer移植插件到Dancer2
移植关键字
- 使关键字成为独立的
sub
,而不是register
的参数 - 通过
$self
而不是DSL关键字来访问数据 - 将
register_plugin
更改为plugin_keywords
移植钩子
- 添加一个
BUILD
函数 - 将钩子函数移动到
BUILD
中,或从BUILD
中引用它们 - 将每个钩子函数包装在
$self->dsl->hook
调用中
移植测试
- 导入Plack::Test和HTTP::Request::Common而不是Dancer::Test
- 为待测试的应用程序添加一个
package
语句 - 创建一个代表应用程序的Plack::Test实例
- 使用HTTP::Request::Common方法创建请求
- 将
$response->{content}
更改为$response->content
标签
Christopher White
Chris White是一位经验丰富且多产的发明家、公众演讲者、专利代理人、计算机工程师、demoscener和软件开发者。他目前为D3 Engineering构建嵌入式Linux系统。他间或撰写关于技术、音乐和奶酪的博客。
浏览他的文章
反馈
这篇文章有什么问题吗?请在GitHub上打开一个问题或拉取请求,帮助我们。