2009-11-18 6 views
7

मैं कक्षा में कुछ एक्सेसर विधियों के आसपास कुछ रैपर व्यवहार लागू करने के लिए Mooseroles का उपयोग कर रहा हूं। मैं इस भूमिका को कई मॉड्यूल पर लागू करना चाहता हूं, जिनमें से प्रत्येक के पास विशेषताओं का एक अलग सेट है जिसका एक्सेसर मैं लपेटना चाहता हूं। क्या भूमिका के भीतर से मॉड्यूल के मेटा वर्ग को लागू करने का कोई तरीका है? इस तरह अर्थात कुछ:मैं मॉड्यूल की मेटा क्लास को कैसे एक्सेस कर सकता हूं, मेरी मूस भूमिका पर लागू किया जा रहा है?

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; 

उत्तर

8

ऐसा लगता है कि MooseX::Role::Parameterized तरह चाल करना होगा:

साधारण भूमिकाओं की आवश्यकता हो सकती है कि अपने उपभोक्ताओं विधि के नाम की एक विशेष सूची है। चूंकि पैरामीटरयुक्त भूमिकाओं के उपभोक्ता तक सीधे पहुंच है, इसलिए आप इसका निरीक्षण कर सकते हैं और उपभोक्ता आपकी आवश्यकताओं को पूरा नहीं करते हैं तो त्रुटियों को फेंक सकते हैं। (link)

भूमिका विशेषज्ञता का विवरण वर्ग से बढ़ाया गया है; इसे किसी भी पैरामीटर को पास करने की आवश्यकता नहीं है, जिसे यह जानने के लिए आवश्यक है कि भूमिका पारित करने के लिए पैरामीटर (लपेटने के लिए फ़ील्ड की सूची) क्या है। एकमात्र कुंजी यह है कि भूमिका के बाद उपयोग की जानी चाहिए, प्रासंगिक गुण वर्ग पर परिभाषित किए गए हैं।

इसलिए, भस्म वर्ग और भूमिका बन इतनी तरह परिभाषित:

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; 

परिशिष्ट: मैं ने पाया है कि यदि एक पैरामिट्रीकृत भूमिका एक और पैरामिट्रीकृत भूमिका है, तो $target_meta नेस्टेड भूमिका में खपत वास्तव में उपभोग वर्ग (आईएसए Moose::Meta::Class) के मेटा-क्लास की बजाय मूल भूमिका (आईएसए MooseX::Role::Parameterized::Meta::Role::Parameterized) की मेटा-क्लास होगी। उचित मेटा-क्लास व्युत्पन्न होने के लिए, आपको इसे पैरामीटर के रूप में स्पष्ट रूप से पास करने की आवश्यकता है। मैं एक "सबसे अच्छा अभ्यास" टेम्पलेट के रूप में मेरे सभी पैरामिट्रीकृत भूमिकाओं को यह जोड़ लिया है:

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; 

परिशिष्ट 2: मैं आगे पता चला है कि भूमिका एक वस्तु दृष्टान्त करने के लिए लागू किया जा रहा है अगर, के रूप में एक वर्ग के लिए विरोध किया, तो भूमिका में $target_meta वास्तव में वस्तु के वर्ग उपभोक्ता कर हो जाएगा:

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 

इसलिए, इस कोड के लिए आवश्यक है जब निकालने पैरामीटरयुक्त भूमिका की शुरुआत में मेटा-क्लास:

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

यह मॉड्यूल के लिए लिखे गए कार्यों में से एक है। – perigrin

+2

नोट: मैं अब उपर्युक्त "सर्वोत्तम अभ्यास" पर विचार नहीं करता हूं, और वास्तव में एमएक्सआरपी के इस (एबी) उपयोग को दोबारा प्रतिक्रिया देता हूं। आईएमएचओ अगर आपको भूमिका के भीतर से '$ मेटा' तक पहुंचने की ज़रूरत है, तो आपके डिजाइन में कुछ बदबूदार है। – Ether

संबंधित मुद्दे

 संबंधित मुद्दे