将 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插件的更多信息

快速参考:从Dancer移植插件到Dancer2

移植关键字

  • 使关键字成为独立的sub,而不是register的参数
  • 通过$self而不是DSL关键字来访问数据
  • register_plugin更改为plugin_keywords

移植钩子

  • 添加一个BUILD函数
  • 将钩子函数移动到BUILD中,或从BUILD中引用它们
  • 将每个钩子函数包装在$self->dsl->hook调用中

移植测试

  • 导入Plack::TestHTTP::Request::Common而不是Dancer::Test
  • 为待测试的应用程序添加一个package语句
  • 创建一个代表应用程序的Plack::Test实例
  • 使用HTTP::Request::Common方法创建请求
  • $response->{content}更改为$response->content

标签

Christopher White

Chris White是一位经验丰富且多产的发明家、公众演讲者、专利代理人、计算机工程师、demoscener和软件开发者。他目前为D3 Engineering构建嵌入式Linux系统。他间或撰写关于技术、音乐和奶酪的博客

浏览他的文章

反馈

这篇文章有什么问题吗?请在GitHub上打开一个问题或拉取请求,帮助我们。