2010-12-13 13 views
5

यह मूस संरचित प्रकारों के बारे में मेरे previous question से चलता है। प्रश्न की लंबाई के लिए मैं क्षमा चाहता हूं। मैं यह सुनिश्चित करना चाहता था कि मैंने सभी आवश्यक विवरण शामिल किए हैं।मूस जबरन और बिल्डर्स

MyApp::Type::Field संरचित प्रकार को परिभाषित करता है। मैं अपने विशेषता को Person कक्षा से अधिक आसानी से सेट करने की अनुमति देने के लिए मजबूरता का उपयोग करता हूं (नीचे उदाहरण देखें)। ध्यान दें कि मेरे असली एप्लिकेशन में, जहां फ़ील्ड प्रकार का उपयोग किसी व्यक्ति के नाम से अधिक के लिए किया जाता है, मैं भी हैशफ से सहभागिता करता हूं।

मैं भी स्थापित करने की आवश्यकता MyApp::Type::Fieldsize और required केवल पढ़ने के लिए निर्माण समय में MyApp::Person से जिम्मेदार बताते हैं। मैं इसे बिल्डर विधि का उपयोग करके कर सकता हूं, लेकिन अगर इसे मजबूर किया जाता है, तो इसे मजबूर नहीं किया जाता है, क्योंकि मेरा जबरदस्त बिल्डर विधि का उपयोग किए बिना सीधे एक नई वस्तु बनाता है।

मैं around विधि संशोधक MyApp::Person (नीचे उदाहरण देखें) जोड़कर इसे गोल कर सकता हूं, लेकिन यह गन्दा लगता है। around विधि संशोधक को अक्सर बार कहा जाता है, लेकिन मुझे केवल पढ़ने-योग्य विशेषताओं को एक बार सेट करने की आवश्यकता होती है।

क्या ऐसा करने का कोई बेहतर तरीका है, जबकि अभी भी जबरन अनुमति है? MyApp::Type::Field कक्षा size और required को डिफ़ॉल्ट या बिल्डरों के माध्यम से प्रारंभ नहीं कर सकती है, क्योंकि इसका कोई तरीका नहीं है कि मूल्य क्या होना चाहिए।

यह केवल मामला हो सकता है कि मैं around संशोधक के पक्ष में मजबूरता से गुजरता हूं।

MyApp::Type::Field

coerce 'MyApp::Type::Field' 
    => from 'Str' 
     => via { MyApp::Type::Field->new(value => $_) }; 

has 'value' => (is => 'rw'); 
has 'size'  => (is => 'ro', isa => 'Int', writer => '_set_size',  predicate => 'has_size'); 
has 'required' => (is => 'ro', isa => 'Bool', writer => '_set_required', predicate => 'has_required'); 

MyApp::Person

has name => (is => 'rw', isa => 'MyApp::Type::Field', lazy => 1, builder => '_build_name', coerce => 1);  

sub _build_name { 
    print "Building name\n"; 
    return MyApp::Type::Field->new(size => 255, required => 1); 
} 

MyApp::Test

print "Create new person with coercion\n"; 
my $person = MyApp::Person->new(); 
print "Set name\n"; 
$person->name('Joe Bloggs'); 
print "Name set\n"; 
printf ("Name: %s [%d][%d]\n\n", $person->name->value, $person->name->size, $person->name->required); 

print "Create new person without coercion\n"; 
$person = MyApp::Person->new(); 
print "Set name\n"; 
$person->name->value('Joe Bloggs'); 
print "Name set\n"; 
printf ("Name: %s [%d][%d]\n\n", $person->name->value, $person->name->size, $person->name->required); 

प्रिंटों:

Create new person with coercion 
Set name 
Name set 
Name: Joe Bloggs [0][0] 

Create new person without coercion 
Set name 
Building name 
Name set 
Name: Joe Bloggs [255][2] 

MyApp::Person के लिए एक around विधि आपरिवर्तक जोड़ें, और बदल बिल्डर इतना है कि यह निर्धारित नहीं करता है size और required:

around 'name' => sub { 
    my $orig = shift; 
    my $self = shift; 

    print "Around name\n"; 

    unless ($self->$orig->has_size) { 
     print "Setting size\n"; 
     $self->$orig->_set_size(255); 
    }; 

    unless ($self->$orig->has_required) { 
     print "Setting required\n"; 
     $self->$orig->_set_required(1); 
    }; 

    $self->$orig(@_); 
}; 

sub _build_name { 
    print "Building name\n"; 
    return MyApp::Type::Field->new(); 
} 

जब MyApp::Test चलाया जाता है, size और required हैं दो बार सेट करें।

Create new person with coercion 
Set name 
Around name 
Building name 
Setting size 
Setting required 
Name set 
Around name 
Setting size 
Setting required 
Around name 
Around name 
Name: Joe Bloggs [255][3] 

Create new person without coercion 
Set name 
Around name 
Building name 
Name set 
Around name 
Around name 
Around name 
Name: Joe Bloggs [255][4] 

प्रस्तावित समाधान

daotoad's प्रत्येक MyApp::Person विशेषता के लिए एक उप-प्रकार बनाने, और एक MyApp::Type::Field कार्यों में एक Str से कि उप-प्रकार coercing काफी अच्छी तरह से के सुझाव। मैं लूप में पूरे लॉट को लपेटकर कई उपप्रकार, जबरन और विशेषताओं को भी बना सकता हूं। यह समान गुणों के साथ कई विशेषताओं को बनाने के लिए बहुत उपयोगी है।

नीचे दिए गए उदाहरण में, मैंने handles का उपयोग करके प्रतिनिधिमंडल स्थापित किया है, ताकि $person->get_first_name का अनुवाद $person->first_name->value पर किया जा सके।

package MyApp::Type::Field; 

use Moose; 

has 'value'  => (
    is   => 'rw', 
); 

has 'size'  => (
    is   => 'ro', 
    isa   => 'Int', 
    writer  => '_set_size', 
); 

has 'required' => (
    is   => 'ro', 
    isa   => 'Bool', 
    writer  => '_set_required', 
); 

__PACKAGE__->meta->make_immutable; 
1; 

package MyApp::Person; 
use Moose; 
use Moose::Util::TypeConstraints; 
use namespace::autoclean; 

{ 
    my $attrs = { 
     title  => { size => 5, required => 0 }, 
     first_name => { size => 45, required => 1 }, 
     last_name => { size => 45, required => 1 }, 
    }; 

    foreach my $attr (keys %{$attrs}) { 

     my $subtype = 'MyApp::Person::' . ucfirst $attr; 

     subtype $subtype => as 'MyApp::Type::Field'; 

     coerce $subtype 
      => from 'Str' 
       => via { MyApp::Type::Field->new(
        value => $_, 
        size  => $attrs->{$attr}{'size'}, 
        required => $attrs->{$attr}{'required'}, 
       ) }; 

     has $attr => (
      is  => 'rw', 
      isa  => $subtype, 
      coerce => 1, 
      writer => "set_$attr", 
      handles => { "get_$attr" => 'value' }, 
      default => sub { 
       MyApp::Type::Field->new(
        size  => $attrs->{$attr}{'size'}, 
        required => $attrs->{$attr}{'required'}, 
       ) 
      }, 
     ); 
    } 
} 

__PACKAGE__->meta->make_immutable; 
1; 

package MyApp::Test; 

sub print_person { 
    my $person = shift; 

    printf "Title:  %s [%d][%d]\n" . 
      "First name: %s [%d][%d]\n" . 
      "Last name: %s [%d][%d]\n", 
      $person->title->value || '[undef]', 
      $person->title->size, 
      $person->title->required, 
      $person->get_first_name || '[undef]', 
      $person->first_name->size, 
      $person->first_name->required, 
      $person->get_last_name || '[undef]', 
      $person->last_name->size, 
      $person->last_name->required; 
} 

my $person; 

$person = MyApp::Person->new(
    title  => 'Mr', 
    first_name => 'Joe', 
    last_name => 'Bloggs', 
); 

print_person($person); 

$person = MyApp::Person->new(); 
$person->set_first_name('Joe'); 
$person->set_last_name('Bloggs'); 

print_person($person); 

1; 

प्रिंटों: एक लेखक देता है, एक बराबर सेटर प्रदान करता है वर्ग काफी साफ करने के लिए इंटरफ़ेस बनाने जोड़ना

Title:  Mr [5][0] 
First name: Joe [45][6] 
Last name: Bloggs [45][7] 
Title:  [undef] [5][0] 
First name: Joe [45][8] 
Last name: Bloggs [45][9] 

उत्तर

3

हर व्यक्ति name क्षेत्र के लिए विभिन्न आवश्यकताओं करने जा है? यह असंभव लगता है।

ऐसा लगता है कि आपके पास प्रत्येक Field के लिए पैरामीटर का एक सेट है। तो फील्ड के उप प्रकार के रूप में एक व्यक्ति का नाम परिभाषित करें। आपका दबाव स्ट्रिंग से पर्सननाम तक होगा। फिर जबरन कोड और Field->new() पर कॉल करते समय आवश्यक मानों को उचित और लंबाई पर लागू कर सकते हैं।

इसके अलावा, ऐसा लगता है कि आप एक मूस ऑब्जेक्ट के लिए एक विशेषता वस्तु बना रहे हैं, जो मेटा-ऑब्जेक्ट सिस्टम पर आधारित है जो पहले से ही विशेषता ऑब्जेक्ट प्रदान करता है। अपने खुद के बनाने के बजाए अपनी विशेषता वस्तु का विस्तार क्यों न करें?

इस दृष्टिकोण पर अधिक जानकारी के लिए Moose Cookbook Meta Recipes देखें।

+1

फ़ील्ड एक मूसएक्स :: प्रकार :: मेटा-एट्रिब्यूट्स के साथ एक विशेषता से संरचित है। उपयोग का एक उदाहरण एक वेब फॉर्म है जहां प्रत्येक फ़ील्ड को एक मान, अधिकतम लंबाई (आकार) और एक आवश्यक ध्वज की आवश्यकता होती है। मॉडल (व्यक्ति वर्ग, इस उदाहरण में), आकार और आवश्यक ध्वज सेट करता है। इसलिए 'फील्ड', काफी सामान्य होने के लिए है, जबकि' व्यक्ति वर्ग 'अधिक विशिष्ट है। मैंने पहले मेटा-एट्रिब्यूट्स को देखा, लेकिन उदाहरण के लिए, वे ''$ person-> मेटा-> get_attribute (' name ') -> size()') तक पहुंचने के लिए थोड़ा अजीब हैं। एक उप प्रकार एक विकल्प हो सकता है। मैं इसे देख लूंगा ... – Mike

+0

मैंने अभी एक उप प्रकार बनाने के साथ प्रयोग किया है, और लगता है कि यह एक अच्छा समाधान प्रदान कर सकता है। मैं कल कुछ और परीक्षण करूँगा ... धन्यवाद। – Mike

+0

मैंने अपने उत्तर को एक प्रस्तावित समाधान के साथ अपडेट किया है जो आपके उप प्रकार के सुझाव का उपयोग करता है। आपके सुझाव के लिए धन्यवाद। – Mike

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