2009-11-18 17 views
7

Tôi đang sử dụng Mooseroles để áp dụng một số hành vi trình bao bọc xung quanh một số phương thức truy cập trong một lớp học. Tôi muốn áp dụng vai trò này cho một số mô-đun, mỗi mô-đun có một tập hợp thuộc tính khác nhau có trình truy cập mà tôi muốn bao bọc. Có cách nào để truy cập vào lớp meta của mô-đun đang được áp dụng, từ bên trong vai trò không? ví dụ: một cái gì đó như thế này:Làm cách nào để truy cập vào lớp meta của mô-đun mà vai trò Moose của tôi đang được áp dụng?

package My::Foo; 
use Moose; 
with 'My::Role::X'; 

has [ qw(attr1 attr2) ] => (
    is => 'rw', # ... 
); 

has 'fields' => (
    is => 'bare', isa => 'ArrayRef[Str]', 
    default => sub { [qw(attr1 attr2) ] }, 
); 
1; 

package My::Role::X; 
use Moose::Role; 

# this should be a Moose::Meta::Class object 
my $target_meta = '????'; 

# get Class::MOP::Attribute object out of the metaclass 
my $fields_attr = $target_meta->find_attribute_by_name('fields'); 

# extract the value of this attribute - should be a coderef 
my $fields_to_modify = $fields_attr->default; 

# evaluate the coderef to get the arrayref 
$fields_to_modify = &$fields_to_modify if ref $fields_to_modify eq 'CODE'; 

around $_ => sub { 
    # ... 
} for @$fields_to_modify; 
1; 

Trả lời

8

Dường như MooseX::Role::Parameterized sẽ làm các trick:

vai trò thông thường có thể yêu cầu người tiêu dùng của mình có một danh sách cụ thể của tên phương pháp. Vì các vai trò được tham số hóa có quyền truy cập trực tiếp vào người tiêu dùng, bạn có thể kiểm tra và ném lỗi nếu người tiêu dùng không đáp ứng nhu cầu của bạn. (link)

Chi tiết về vai trò chuyên môn được lưu giữ trong lớp học được tăng cường; nó thậm chí không cần phải vượt qua bất kỳ tham số tất cả nó cần biết là những gì các thông số (danh sách các lĩnh vực để bọc) để vượt qua vai trò. Chìa khóa duy nhất là vai trò phải được sử dụng sau khi các thuộc tính có liên quan đã được xác định trên lớp.

Do đó, lớp tiêu thụ và vai trò trở thành định nghĩa như sau:

package My::Foo; 
use Moose; 

my @fields = qw(attr1 attr2); 

has \@fields => (
    is => 'rw', # ... 
); 

has 'fields' => (
    is => 'bare', isa => 'ArrayRef[Str]', 
    default => sub { \@fields }, 
); 

with 'My::Role::X' => {}; 

1; 

package My::Role::X; 
use MooseX::Role::Parameterized; 

role { 
    my $p = shift; 

    my %args = @_; 

    # this should be a Moose::Meta::Class object 
    my $target_meta = $args{consumer}; 

    # get Class::MOP::Attribute object out of the metaclass 
    my $fields_attr = $target_meta->find_attribute_by_name('fields'); 

    # extract the value of this attribute - should be a coderef 
    my $fields_to_modify = $fields_attr->default; 

    # evaluate the coderef to get the arrayref 
    $fields_to_modify = &$fields_to_modify if ref $fields_to_modify eq 'CODE'; 

    around $_ => sub { 
     # ... 
    } for @$fields_to_modify; 
}; 

1; 

Phụ Lục: Tôi đã phát hiện ra rằng nếu một vai trò tham số tiêu thụ một vai trò tham số, sau đó $target_meta trong vai trò lồng nhau sẽ thực sự là siêu lớp của vai trò cha mẹ (isa MooseX::Role::Parameterized::Meta::Role::Parameterized), thay vì lớp meta của lớp tiêu thụ (isa Moose::Meta::Class). Để có được lớp meta thích hợp, bạn cần phải chuyển nó thành một tham số. Tôi đã thêm này cho tất cả các vai trò tham số của tôi như là một "thực hành tốt nhất" mẫu:

package MyApp::Role::SomeRole; 

use MooseX::Role::Parameterized; 

# because we are used by an earlier role, meta is not actually the meta of the 
# consumer, but of the higher-level parameterized role. 
parameter metaclass => (
    is => 'ro', isa => 'Moose::Meta::Class', 
    required => 1, 
); 

# ... other parameters here... 

role { 
    my $params = shift; 
    my %args = @_; 

    # isa a Moose::Meta::Class 
    my $meta = $params->metaclass; 

    # class name of what is consuming us, om nom nom 
    my $consumer = $meta->name; 

    # ... code here... 

}; # end role 
no Moose::Role; 
1; 

Phụ Lục 2: Tôi đã phát hiện thêm rằng nếu vai trò đang được áp dụng cho một ví dụ đối tượng, như trái ngược với một lớp, sau đó $target_meta trong vai trò thực sự sẽ là lớp của đối tượng làm tiêu thụ:

package main; 
use My::Foo; 
use Moose::Util; 

my $foo = My::Foo->new; 
Moose::Util::apply_all_roles($foo, MyApp::Role::SomeRole, { parameter => 'value' }); 

package MyApp::Role::SomeRole; 
use MooseX::Role::Parameterized; 
# ... use same code as above (in addendum 1): 

role { 
    my $meta = $args{consumer}; 
    my $consumer = $meta->name;  # fail! My::Foo does not implement the 'name' method 

Do đó, mã này là cần thiết khi giải nén meta-class khi bắt đầu vai trò được tham số hóa:

role { 
    my $params = shift; 
    my %args = @_; 

    # could be a Moose::Meta::Class, or the object consuming us 
    my $meta = $args{consumer}; 
    $meta = $meta->meta if not $meta->isa('Moose::Meta::Class'); # <-- important! 
+0

Đây là một trong những điều mà mô-đun được viết. – perigrin

+2

Lưu ý: Tôi không còn xem xét "thực hành tốt nhất" ở trên, và thực sự đã tái cấu trúc tất cả (ab) việc sử dụng MXRP này. IMHO nếu bạn cần truy cập '$ meta' từ bên trong vai trò, bạn có thứ gì đó bốc mùi trong thiết kế của mình. – Ether