Ça faisait quelques semaines que cette version trainait dans mes cartons, je suppose qu’inconsciemment j’attendai une vraie raison de publier une nouvelle version (comprendre : une belle ligne dans le Changelog).
Rached me l’a fournie en fin de journée au boulot : il a mis le doigt sur une brêche dans les perfs de Coat lorsque l’on utilise la coercition (coerce dans Coat). La chute de perfs était due à une utilisation de Carp::confess, catchée par un bloc eval, ce qui est très mal, car à chaque fois que Perl utilise confess, il va chercher toutes les infos de la pile d’appel, ce qui est couteux.
Le mécanisme de coercition de Coat utilise désormais une méthode silencieuse qui retourne un booléen au lieu de faire un confess, on évite ainsi de ruiner les performances.
Outre ce correctif important, la version 0.334 apporte le support de BUILDARGS qui vous permet de construire vos arguments lors de l’instanciation d’objets.
Je viens d’uploader le package sur CPAN, donc d’ici quelques heures ça devrait être dispo ici.
EDIT : 25 Nov. 2008, 10:07
Après quelques retours sur ce billet je m’aperçois que je parle trop vite et que certains ont eu la curiosité éveillée, je m’empresse de préciser donc :
Qu’est-ce que la coercition
La coercition est un mécanisme qui permet de transformer à la volée une valeur d’un champ dans un format particulier. La coercition est utilisée pour modifier une valeur qui ne valide pas le type d’un attribut mais qui valide un type défini dans une coercition.
Un exemple simple : j’ai une classe qui possède un attribut “mysql_date” qui est valdie le format : YYYY-MM-DD. Je peux définir une coercition sur cet attribut pour toute valeur qui validerait le type ‘Timestamp’ par exemple.
Ainsi je pourrai faire
$mon_objet->mysql_date( time() );
La coercition entrera donc en jeu et convertira l’entier retourné par time() en une date formatée selon la règle définie par l’utilisateur.
Quel était le problème avec confess/eval ? Est-ce mal d’utiliser confess ?
La chute de performance n’était pas due directement à confess, mais plutôt à l’utilisation que le mécanisme de coercition en faisait. Lorsqu’une valeur ne validait pas le type premier d’un attribut et qu’une coercition était définie, Coat bouclait sur toutes les coercitions définies afin de voir si la valeur validait le type source de la coercition. Or cette validation était faite avec le mécanisme standard de Coat qui déclenche une exception avec confess si la valeur n’est pas valide. Coat utilisait donc un bloc de code dans eval pour savoir si oui ou non la valeur est acceptée.
C’est ici que le problème réside : on ne veut pas d’exception, on veut une réponse booléene, inutile donc de faire appel à confess (et donc de lire la pile d’appel), il nous faut une méthode de validation silencieuse, qui retourne 1 ou 0 selon la validité du champ.
On peut donc résumer en disant que confess ne pose aucun problème tant qu’il est utilisé uniquement pour interrompre l’exécution du programme, et surtout pas pour être catché afin d’avoir une réponse à une question booléene.
Pour les gens intéressés, le diff est dispo ici, on constate également que la construction du message en cas d’exception était également couteuse, puisqu’elle fait appel a un bloc de code (les messages d’erreur de coercition sont des blocs de code, afin de pouvoir expander $_).
Qu’est-ce que BUILDARGS
Cette fonctionnalité vous permet de définir une méthode BUILDARGS dans votre classe. Lorsqu’un objet de cette classe est instancié, la méthode est appelée avec les arguments donnés à l’instanciation. Elle peut à loisir modifier les arguments et la valeur qu’elle retourne sera la structure de données finale reçue par l’instanciation.
On peut dire qu’il s’agit un pre-processing des arguments d’instanciation. Combiné avec une méthode BUILD ça peut être très combo-magique :-)
Voilà, j’éspère que ces précisions sont claires (je n’en suis pas sûr: c’est le matin et je n’ai pas bu mon café). Les commentaires sont là si vous avez des questions.
Chouette, je m’en va mettre à jour mon paletot préféré.
s’eut ete de bon aloi un petit exemple sur BUILDARG
Merci au mainteneur pour tant de réactivité ! :)
@Rached you’re welcome
@melkor Voici un exemple concret d’utilisation de BUILDARGS :
On a une classe qui a un champ “file”, on veut pouvoir instancier l’objet avec les arguments reçu de la ligne de commande. Donc on veut automatiquement traiter un argument unique comme étant une valeur pour le champ file.
En clair : on veut qu’un Class->new(“toto”) soit compris comme un Class->new(file => “toto”).
Voici comment faire avec BUILDARGS :
package ConfReader; use Coat; has file => ( is => 'rw', isa => 'Str', ); # Si on ne reçoit qu'un seul argument, on considère # que c'est "file" sub BUILDARGS { my ($self, @args) = @_; return (@args == 1) ? {file => $args[0]} : @args; } package main; my $conf = ConfReader->new(@ARGV);Mais comment c’est bien quand tu parles de Perl toi. :)
(et merci pour les explications)
@oz Heu, merci :)
Gasp !
Ici on à la 0.1_0.6
C’est grave docteur ?
@Jean-Charles
Ouh la ! C’est vieux ! :-) Disons que si vous upgradez, vous aurez pas mal de corrections de bugs, d’amélioration de perfs sur certaines fonctionnalités et de nouvelles possibilités (définition de types maison notamment).