Wait the light to fall

第十三天 - 承担角色

焉知非鱼

img

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

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

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

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

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 时所需的一切:

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::,然后是我想要的短名称。当我应用这个时,我可以省去大部分包裹名称,并使用前面加号的短名称:

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

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

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

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

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

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

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

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

my $file = path( '/Users/brian/bin/interesting.txt' );

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

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

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天,虽然我不认为乔尔或我是否足够聪明来计划。