第十三天 - 承担角色

img

在我之前的 Advent 文章中,我创建了高阶 promise并向您展示了如何使用它们。我没有告诉你它们如何工作的魔力。现在我将从另一个方向开发另一个例子。

有时候我希望 Mojo::File 的行为有点不同。我经常有一个路径,我想只将基本名称与不同的目录结合起来。我最终为两者制作 Mojo::File 对象,然后使用目录对象来获得我想要的东西:

1
2
3
4
5
6
use Mojo::File qw(path);
my $path = Mojo::File->new( '/Users/brian/bin/interesting.txt' );
my $dir = Mojo::File->new( '/usr/local/bin' );
my $new_path = $dir->child( $path->basename );

say $new_path; # /usr/local/bin/interesting.txt

那很烦人。我不喜欢它需要这么多步骤。我想要一些方法。我宁愿能够像这样编写它,我从有趣的文件开始并继续处理它而不是切换到其他对象:

1
2
3
4
5
6
7
use Mojo::File qw(path);

my $new_path = Mojo::File
->new( '/Users/brian/bin/interesting.txt' )
->rebase( '/usr/local/bin' ); # this isn't a method

say $new_path; # /usr/local/bin/interesting.txt

我可以通过各种 Perl 技巧通过猴子补丁或子类化将此方法添加到 Mojo::File。但是,像往常一样,Mojolicious 期待我的愿望并提供一种方法来做到这一点。我可以添加一个角色,

当我跳进它时,你可以自己阅读角色。首先,我创建一个代表我的角色的类。我定义了我想要的方法。我使用我想要影响的包的名称,添加 ::Role::,然后我想使用的名称;它的小写并不重要。 Mojo::Base 设置了当我导入 -role 时所需的一切:

1
2
3
4
5
6
7
package Mojo::File::Role::rebase {
use Mojo::Base qw(-role -signatures);

sub rebase ($file, $dir) {
$file->new( $dir, $file->basename )
}
}

我在我想要影响的类上使用 with_roles 来应用我的新功能。因为我使用命名约定通过在它前面加上目标类(Mojo::File),然后是 ::Role::,然后是我想要的短名称。当我应用这个时,我可以省去大部分包裹名称,并使用前面加号的短名称:

1
my $file_class = Mojo::File->with_roles( '+rebase' );

或者我可以输入完整的包名:

1
2
my $file_class = Mojo::File->with_roles(
'Mojo::File::Role::rebase' );

如果我没有遵循命名约定,我需要使用它:

1
2
my $file_class = Mojo::File->with_roles(
'I::Totally::Rejected::The::Convention::rebase' );

$file_class 是具有新类名的字符串。在那个课程的后面有一些多重继承魔法,你会更乐意忽略它。我不需要使用裸字类名来调用类方法;标量变量中的字符串也可以正常工作。现在我可以使用我的 rebase

1
2
3
say $file_class
->new( '/Users/brian/bin/interesting.txt' )
->rebase( '/usr/local/bin/' );

这比我之前做的要干净得多,我喜欢它的流动方式。但是,如果我从其他东西获得已经创建的 Mojo::File 对象怎么办?我也可以应用 ad hoc 角色:

1
2
3
4
5
my $file = path( '/Users/brian/bin/interesting.txt' );

say $file
->with_roles( '+rebase' )
->rebase( '/usr/local/bin/' );

我可以走得更远我添加到我的角色的任何方法都成为该类的一部分。我经常想要获取文件的摘要,虽然 Mojo::Util 通过一些便利功能使这更容易,但我想要更方便。我为我的角色添加了几种方法来为我做啜食:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use Mojo::File;

package Mojo::File::Role::MyUtils {
use Mojo::Base qw(-role -signatures);
use Mojo::Util qw(md5_sum sha1_sum);

sub rebase ($file, $dir) {
$file->new( $dir, $file->basename )
}

sub md5 ($file) {
md5_sum( $file->slurp )
}

sub sha1 ($file) {
sha1_sum( $file->slurp )
}
}

my $file = Mojo::File
->with_roles( '+MyUtils' )
->new(shift);

say $file->sha1;
say $file->md5;

您可以在 Joel Berger 的 2017 Mojolicious Advent Calendar 条目第13天:更多关于角色中阅读更多关于角色的信息。奇怪的是那也是在第13天,虽然我不认为乔尔或我是否足够聪明来计划。