2010-04-01 5 views
6

J'utilise actuellement un bloc eval pour tester que j'ai défini un attribut en lecture seule. Existe-t-il un moyen plus simple de faire cela?Existe-t-il un moyen simple de tester si un attribut Moose est en lecture seule?

Exemple de code de travail:

#Test that sample_for is ready only 
eval { $snp_obj->sample_for('t/sample_manifest2.txt');}; 
like([email protected], qr/read-only/xms, "'sample_for' is read-only"); 



MISE À JOUR

Merci à Friedo, l'éther et Robert P pour leurs réponses et à l'éther, Robert P, et jrockway pour leurs commentaires.

J'aime la façon dont la réponse de l'éther assure que $is_read_only est seulement une valeur vraie ou fausse (à savoir, mais jamais un coderef) en niant avec un !. Double negation fournit également cela. Ainsi, vous pouvez utiliser $is_read_only dans une fonction is(), sans imprimer le coderef.

Voir la réponse de Robert P ci-dessous pour la réponse la plus complète. Tout le monde a été très utile et s'est appuyé sur les réponses et les commentaires des uns et des autres. Dans l'ensemble, je pense qu'il m'a aidé le plus, d'où son est maintenant marqué la réponse acceptée. Encore une fois, merci à Ether, Robert P, friedo et jrockway.



Si vous demandez peut-être, comme je l'ai fait tout d'abord, voici la documentation sur la différence entre get_attribute et find_attribute_by_name (from Class::MOP::Class):

$metaclass->get_attribute($attribute_name) 

    This will return a Class::MOP::Attribute for the specified $attribute_name. If the 
    class does not have the specified attribute, it returns undef. 

    NOTE that get_attribute does not search superclasses, for that you need to use 
    find_attribute_by_name. 
+0

Ce serait mieux écrit que 'ok (snp_obj $ -> meta-> get_attribute ('sample_for') -> get_write_method(), "'sample_for' est en lecture seule"); - en cas d'échec du test, 'is()' affiche le 2ème argument (qui serait un coderef) .. sans mentionner que vous avez les 1er et 2ème arguments inversés: 'is ($ has, $ expected, $ test_name)'. – Ether

+0

Si votre tableau @attribute_names est construit avec soin, ça devrait aller; mais si l'attribut n'existe pas, vous exploserez :) –

+0

+1 pour avoir remarqué comment localiser l'attribut dans la superclasse – user1027562

Répondre

5

Techniquement, un attribut n'a pas besoin d'avoir une méthode de lecture ou d'écriture. La plupart du temps, mais pas toujours. Un exemple (gracieusement volé jrockway's comment) est ci-dessous:

has foo => ( 
    isa => 'ArrayRef', 
    traits => ['Array'], 
    handles => { add_foo => 'push', get_foo => 'pop' } 
) 

Cet attribut existe, mais pas standards lecteurs et écrivains. Par conséquent, pour tester dans chaque situation qu'un attribut a été défini comme is => 'RO', vous devez vérifier à la fois la méthode d'écriture et la méthode de lecture.Vous pouvez le faire avec ce sous-programme:

# returns the read method if it exists, or undef otherwise. 
sub attribute_is_read_only { 
    my ($obj, $attribute_name) = @_; 
    my $attribute = $obj->meta->get_attribute($attribute_name); 

    return unless defined $attribute; 
    return (! $attribute->get_write_method() && $attribute->get_read_method()); 
} 

Sinon, vous pouvez ajouter une double négation avant la dernière return à boolify la valeur de retour:

return !! (! $attribute->get_write_method() && $attribute->get_read_method()); 
+1

J'aime le premier exemple;) – jrockway

5

Comme indiqué dans Class::MOP::Attribute:

my $attr = $this->meta->find_attribute_by_name($attr_name); 
my $is_read_only = ! $attr->get_write_method(); 

$attr->get_write_method() obtiendrez la méthode de l'écrivain (soit vous cr (ou généré) ou undef s'il n'y en a pas.

+0

Merci! Sachant qu'il retourne 'undef' permet un test d'une ligne (j'ai essayé de le poster ici, mais ça n'a pas l'air très joli). –

+0

Eh bien, en fait ... Cela teste si elle a une méthode d'écriture. Cela ne teste pas si elle a une méthode de lecture cependant. Il ne doit pas avoir non plus, techniquement. Ce n'est pas un attribut très utile si ce n'est pas le cas, mais vous pouvez l'avoir! –

+0

@Robert: Oui, à strictement parler, il vérifie que l'attribut est "non inscriptible" (pas isa => 'rw'), ce qui n'est pas tout à fait le même que "readonly" (isa => 'ro'). – Ether

3

Vous devriez être en mesure d'obtenir cela du métaclasse de l'objet:

unless ($snp_obj->meta->get_attribute('sample_for')->get_write_method) { 
    # no write method, so it's read-only 
} 

Voir Class::MOP::Attribute pour plus.

+0

Merci! C'est ce dont j'avais besoin. –

Questions connexes