Perl中如何向调用方模块自动注入Moose Role?
问题场景
- 已编写
Validation角色(后续会开发多个同类型角色),代码如下:
package Validation; use Moose::Role; sub check_entity { my ( $self, $db_id ) = @_; #some logic my $found_db = sub { #logic verify id present in db return 1; } return $found_db; }
- 已编写自定义工具模块
MyApp::Moose用于编写规范统一的业务模块,当前已实现能力:- 自动导入
strict、warnings、Clone、namespace::autoclean等通用依赖 - 可根据导入参数自动加载
Moose或Moose::Role - 普通类加载完成后自动调用
meta->make_immutable
- 自动导入
- 需求:在
MyApp::Moose的import逻辑中,自动将Validation等角色注入到调用方,使调用方无需手动声明with加载角色,即可直接访问check_entity方法 - 已查阅Moose相关官方文档未找到可行实现方案
- 约束条件:
- 无法提前实例化调用方模块(调用方类实例化存在必填依赖,角色注入逻辑本身不需要依赖类实例)
- 后续需要用相同方式注入更多自定义角色
- 现有
MyApp::Moose代码如下:
package MyApp::Moose; use strict; use warnings; use namespace::autoclean; use Hook::AfterRuntime; use Import::Into; use Moose (); use Clone 'clone'; sub import { my ($class, @opts) = @_; my $caller = caller; my %opt = map { $_ => 1 } @opts; strict->import::into($caller); warnings->import(); Clone->import::into($caller,'clone'); if($opt{role}) { require Moose::Role; Moose::Role->import({into=>$caller}); } else { Moose->import({into=>$caller}); after_runtime { $caller->meta->make_immutable(); }; } namespace::autoclean->import( -cleanee => $caller, ); return; } 1;
- 期望使用效果:业务模块无需手动加载角色,即可直接调用
check_entity方法,使用示例如下:
package MyApp::Process; use MyApp::Moose; sub some_method { my ($self, $db_id) = @_; # 期望的调用方式 $self->check_entity($db_id) || return; } 1;
实现方案
直接通过Moose元类原生接口应用角色即可,全程不需要实例化类,实现逻辑非常简洁:
- 在
MyApp::Moose中统一定义需要自动注入的角色列表,后续新增角色直接修改该列表即可 - 针对普通类(非角色定义模块),在
after_runtime钩子中,先执行角色应用,再调用make_immutable,避免类设为不可变后无法应用角色的问题 - 角色定义模块(传入
role参数时)不自动注入业务角色,避免角色嵌套污染
修改后的完整MyApp::Moose代码如下:
package MyApp::Moose; use strict; use warnings; use namespace::autoclean; use Hook::AfterRuntime; use Import::Into; use Moose (); use Clone 'clone'; # 统一配置需要自动注入的通用角色,后续新增角色直接添加到该数组 my @DEFAULT_INJECT_ROLES = qw(Validation); sub import { my ($class, @opts) = @_; my $caller = caller; my %opt = map { $_ => 1 } @opts; strict->import::into($caller); warnings->import(); Clone->import::into($caller,'clone'); if($opt{role}) { require Moose::Role; Moose::Role->import({into=>$caller}); } else { Moose->import({into=>$caller}); after_runtime { # 先应用所有默认角色,再将类设为immutable $caller->meta->with(@DEFAULT_INJECT_ROLES); $caller->meta->make_immutable(); }; } namespace::autoclean->import( -cleanee => $caller, ); return; } 1;
说明
- 调用
$caller->meta->with(@roles)是Moose元类提供的标准角色应用接口,直接操作类的元对象,不需要提前实例化类,完全满足约束要求 - 角色应用逻辑放在
after_runtime钩子中执行,保证调用方自身的代码编译完成、元类初始化完毕后再执行角色注入,避免出现类未完全定义就应用角色的异常 - 如果后续需要支持部分类排除指定自动注入角色,可以扩展import参数,例如增加
no_roles配置项,在应用角色前过滤列表即可
内容的提问来源于stack exchange,提问作者Smarhacker
相关产品推荐
相关产品推荐

