2012-05-10 22 views
5

Moose के साथ, आप lazybuilders गुण, जहां बिल्डर विशेषता है जब कहा जाता है पर पहले पहुँचा यदि विशेषता पहले से ही आबादी नहीं किया गया हो सकता है। आप coerce के साथ एक विशेषता का प्रकार जबरन कर सकते हैं, लेकिन यह लागू होता है जब भी विशेषता सेट होती है, इसलिए ऑब्जेक्ट प्रारंभिक पर भी।लेज़ी गुण बलात्कार

मैं आलसी दबाव लागू करने का एक तरीका ढूंढ रहा हूं, जहां एक विशेषता प्रारंभ में आबादी हो सकती है, लेकिन जब इसे पहली बार एक्सेस किया जाता है तो केवल तभी किया जाता है। यह जरूरी है जब जबरदस्त महंगा हो।

निम्न उदाहरण में, मैं एक संघ के प्रकार और विधि संशोधक का उपयोग करते हैं:

package My::Foo; 
use Moose; 
has x => (
    is => 'rw', 
    isa => 'ArrayRef | Int', 
    required => 1 
); 

around "x" => sub { 
    my $orig = shift; 
    my $self = shift; 
    my $val = $self->$orig(@_); 
    unless(ref($val)) { 
     # Do the cocerion 
     $val = [ map { 1 } 1..$val ]; 
     sleep(1); # in my case this is expensive 
    } 
    return $val; 
}; 
1; 

my $foo = My::Foo->new(x => 4); 
is_deeply $foo->x, [ 1, 1, 1, 1 ], "x converted from int to array at call time"; 

लेकिन वहाँ इस के साथ कुछ समस्याएं हैं:

  1. मैं संघ प्रकार नापसंद + विधि संशोधक दृष्टिकोण। यह use coercion instead of unions पर "सर्वोत्तम अभ्यास" सुझाव के खिलाफ चला जाता है। यह घोषणात्मक नहीं है।

  2. मैं कई वर्गों में कई विशेषताओं के साथ ऐसा करने की जरूरत है। इसलिए डीआरवाई के कुछ रूपों की आवश्यकता है। यह मेटा-विशेषता भूमिकाएं, प्रकार-जबरन हो सकता है, आप क्या हैं।

अद्यतन: मैं ikegami's सुझाव पीछा किया एक वस्तु के अंदर महंगा प्रकार बलात्कार संपुटित और इस वस्तु के लिए एक बाहरी दबाव प्रदान करने के लिए:

package My::ArrayFromInt; 
use Moose; 
use Moose::Util::TypeConstraints; 
subtype 'My::ArrayFromInt::Inner', 
    as 'ArrayRef[Int]'; 
coerce 'My::ArrayFromInt::Inner', 
    from 'Int', 
    via { return [ (1) x $_ ] }; 
has uncoerced => (is => 'rw', isa => 'Any', required => 1); 
has value => (
    is  => 'rw', 
    isa  => 'My::ArrayFromInt::Inner', 
    builder => '_buildValue', 
    lazy => 1, 
    coerce => 1 
); 
sub _buildValue { 
    my ($self) = @_; 
    return $self->uncoerced; 
} 
1; 
package My::Foo; 
use Moose; 
use Moose::Util::TypeConstraints; 
subtype 'My::ArrayFromInt::Lazy' => as class_type('My::ArrayFromInt'); 
coerce 'My::ArrayFromInt::Lazy', 
    from 'Int', 
    via { My::ArrayFromInt->new(uncoerced => $_) }; 
has x => (
    is => 'rw', 
    isa => 'My::ArrayFromInt::Lazy', 
    required => 1, 
    coerce => 1 
); 
1; 

यह काम करता है अगर $foo->x->value कहा जाता है। हालांकि यह बिंदु # 2 को हल नहीं करता है, क्योंकि मुझे प्रत्येक विशेषता के लिए My::ArrayFromInt और ::Lazy सबटाइप बनाने की आवश्यकता होगी, जिसे मैं बदलना चाहता हूं। और यदि संभव हो तो मैं $foo->x->value पर कॉल करना से बचना चाहता हूं।

+1

यदि डेटाम का प्रतिनिधित्व करने के दो तरीके हैं, तो कोई भी प्रतिनिधित्व प्राप्त करने में सक्षम होना चाहिए। किसी ऑब्जेक्ट में कॉरर्स करें, फिर उस ऑब्जेक्ट से डेटा को इच्छित प्रारूप में लाएं। [उदाहरण] (http://stackoverflow.com/questions/10506416/can-i-use-an-attribute-modifer-in-moose-in-a-base-class-to-handle-multiple-attri/10508753# 10508753) – ikegami

+0

एस/'मानचित्र {1} 1 .. $ val' /' (1) x $ val'/ – ikegami

+0

@ikegami समस्या यह है कि मजबूती महंगा है; मैं केवल यह करना चाहता हूं अगर विशेषता के लिए पूछा जा रहा है। – devoid

उत्तर

0

कैसे कि रैप करने के लिए ऊपर सहायक विधि के कुछ प्रकार में

की तर्ज पर वस्तुओं की जोड़ी बनाने के लिए वर्णित पंक्तियों के साथ typedef होने, तो कर

has _x => (
    is  => 'ro', 
    isa  => 'Int|MyArrayOfInts', 
    init_arg => 'x', 
    required => 1, 
); 

has x => (
    is => 'ro', 
    lazy => 1, 
    isa => 'MyArrayOfInts', 
    coerce => 1, 
    default => sub { $_[0]->_x }, 
); 

यह मतलब था के बारे में

has_lazily_coerced x => (
    is => 'ro', 
    isa => 'TargetType', 
); 

जो अनचाहे छाया विशेषता के लिए कानूनी प्रकारों की एक सूची प्राप्त करने और आपके लिए विशेषताओं की जोड़ी उत्पन्न करने के लिए टार्गेट टाइप पर आत्मनिरीक्षण करेगा।