2009-09-12 4 views
18

Wenn Sie ein Attribut haben, das jedes Mal geändert werden muss, wenn es gesetzt ist, gibt es eine glatte Möglichkeit, den Accessor selbst zu schreiben und direkt mit dem Inhalt von $self herumzubummeln, wie in diesem Beispiel gemacht?In Moose, wie kann ich ein Attribut ändern, wann immer es eingestellt ist?

package Foo; 
use Moose; 

has 'bar' => (
    isa => 'Str', 
    reader => 'get_bar', 
); 

sub set_bar { 
    my ($self, $bar) = @_; 
    $self->{bar} = "modified: $bar"; 
} 

ich als trigger, aber es schien den gleichen Ansatz zu verlangen.

Arbeitet direkt mit der Hash-Referenz in $self als schlechte Praxis in Moose oder bin ich besorgt über ein Nicht-Problem?

Antwort

8

Ich bin nicht sicher, welche Art von Modifikation benötigen, aber Sie könnten in der Lage sein, zu erreichen, was Sie brauchen, um Verwendung der Typenkorrektur:

package Foo; 
use Moose; 

use Moose::Util::TypeConstraints; 

subtype 'ModStr' 
    => as 'Str' 
    => where { /^modified: /}; 

coerce 'ModStr' 
    => from 'Str' 
    => via { "modified: $_" }; 

has 'bar' => ( 
    isa => 'ModStr', 
    is => 'rw', 
    coerce => 1, 
); 

Wenn Sie diesen Ansatz verwenden, werden nicht alle Werte geändert.

konnte
my $f = Foo->new(); 
$f->bar('modified: bar'); # Set without modification 

Diese Schwäche in Ordnung sein oder es könnte dieser Ansatz unbrauchbar machen: Alles, was die Validierung als ModStr geht direkt verwendet werden. Unter den richtigen Umständen könnte es sogar ein Vorteil sein.

6

Ich denke, mit der Hash-Referenz im trigger wie dies in Ordnung ist:

package Foo; 
use Moose; 

has 'bar' => ( 
    isa => 'Str', 
    is => 'rw', 
    trigger => sub { $_[0]->{bar} = "modified: $_[1]" }, 
); 

Der Auslöser auch ausgelöst, wenn bar arg mit dem Konstruktor übergeben. Dies passiert nicht, wenn Sie eine eigene Methode set_bar oder einen Methodenmodifikator definieren.

re: Hash-Referenz - Generell denke ich, es ist am besten, mit den Attributen Setter/Getter zu bleiben, es sei denn (wie mit oben genannten Trigger) gibt es keine einfache Alternative.

BTW finden Sie diese recent post about triggers von nothingmuch interessant.

+0

Schauen Sie sich die Elche :: Handbuch :: Attribute auf Trigger - http://search.cpan.org/~drolsky/Moose-0.88/lib/Moose/Manual/Attributes.pod#Triggers –

3

Wenn Sie direkt mit dem Hash-Code zu tun haben, können Sie einen anderen Writer angeben und diesen dann innerhalb Ihres eigenen 'public' Writers verwenden.

package Foo; 
use Moose; 

has 'bar' => (
    isa => 'Str', 
    reader => 'get_bar', 
    writer => '_set_bar', 
); 

sub set_bar { 
    my $self = shift; 
    my @args = @_; 
    # play with args; 
    return $self->_set_bar(@args); 
} 

Diese oder Trigger, würde mich schlagen einen guten Ansatz als je nachdem, wann und wie Sie die Argumente manipuliert werden müssen.

(Disclaimer: ungetesteter Code aus dem Speicher geschrieben, das Surfen SO auf einem Netbook mit flockigem Rande Zugang)

+0

Dies ist ein schönes sauber Lösung, obwohl es nicht mit Konstruktoren arbeiten wird. – mikegrb

+0

@mikegrb: Ich bin mir nicht sicher, was genau Sie mit Konstruktoren tun wollen, aber Sie können angeben, wo ein Wert über einen Konstruktor mit dem 'init_arg' Attribut-Modifikator zugewiesen wird, und/oder Sie könnten einige Prüfungen in den' BUILDARGS 'Methode. – Ether

9

Sie können den Methodenmodifikator 'around' verwenden. So etwas wie dieses:

has 'bar' => (
    isa => 'Str', 
    reader => 'get_bar', 
    writer => 'set_bar' 
); 

around 'set_bar' => sub { 
    my ($next, $self, $bar) = @_; 
    $self->$next("Modified: $bar"); 
}; 

Und ja, die Arbeit direkt mit den Hash-Werten gilt als schlechte Praxis.

Bitte gehen Sie auch nicht davon aus, dass die von mir vorgestellte Option unbedingt die richtige ist. Die Verwendung von Subtypen und Zwängen wird die meiste Zeit die richtige Lösung sein - wenn Sie über Ihren Parameter in Bezug auf einen Typ nachdenken, der möglicherweise in Ihrer gesamten Anwendung wiederverwendet werden kann, führt dies zu viel besserem Design, das die Art von beliebigen Modifikationen sein kann mit 'herum' gemacht. Siehe Antwort von @daotoad.

+1

Damit dies funktioniert, müssen Sie beim Definieren des Attributs einen Writer deklarieren. ZB hat 'bar' => (ist => 'rw', isa => 'Str', writer => 'set_bar') – draegtun

+0

re: hash-Werte "schlechte Praxis": Es ist gut in Triggern und Beispielen, die ich habe gesehen (auf Moose Mailing-Liste) bestätigen dies. – draegtun

+0

draegtun: Wenn Moose es nicht geändert hat, ist der Standardschreiber der Attributname. Sie können sich darauf festlegen, wenn Sie den Schreiber nicht angeben möchten. –