Catalyst et Class Workflow

user_icon admin | icon2 Catalyst | icon4 10/10/2009 16h37| Type doc: article| Type File: xml| icon3 No Comment

1. Workflow

Si l'on se réfère à la définition de Wikipédia, il s'agit de la modélisation et la gestion informatique de l'ensemble des tâches à accomplir et des différents acteurs impliqués dans la réalisation d'un processus métier .

Pour un processus de suivi d'appel (Helpdesk), il s'agit de la modélisation des tâches lié à la vie du ticket. Le workflow étant représenté par des états et des transitions entre les états.

Justement, voyons plus précisement le cas d'un suivi d'un bug simpliste:

Etats :

  • 'New' : Etat initial du bug

  • 'Accepted' : Lorsque le bug a été accepté

  • 'Refused' : Lorsque le bug a été réfusé

  • 'Open' : Lorsque le bug est pris en compte

  • 'Closed': Lorsque le bug est fermé

  • 'Unresolved' : Lorsque le bug ne peut être résolu

Transitions :

  • 'Accept' : De New vers 'Accepted'

  • 'Refuse': De New vers Refused

  • 'Take': De Accepted vers Open

  • 'Close': De Open vers Closed

  • 'CantResolve': De Open vers Unresolved

En passant à la moulinette ' GraphViz ' ça nous donne:

Les états sont représentés par les noeuds rouges et les transitions par les liens entre ces noeuds.

Ajoutons à cela que chaque bug aura une instance dans le workflow dont le rôle sera d'enregistrer l'état de ce dernier.

2. Le cas de Class:Workflow

Nous allons voir comment mettre en oeuvre un workflow de type 'suivi d'appel' avec le module Class:Workflow

En tout premier lieu pour utiliser notre workflow nous devrons l'enrichir d'états et de transitions ... mais nous verrons ça plus tard.

Pour faire référence à une instance du workflow, notre bug pourrait être défini ainsi:

        package MyBug;
        use Moose;

        has workflow_instance => (
                does => "Class::Workflow::Instance",
                is   => "rw",
        );
        has description => (
                  ....

Imaginons maintenant que nous avons une action 'accept' dont le rôle est d'accepter le bug. Pour changer l'état de l'instance nous pourrions faire quelque chose comme :

        sub accept {
                my $bug = shift;

                my $wi = $bug->workflow_instance;
                my $current_state = $wi->state;

                # if your state supports named transitions      
                my $accept = $current_state->get_transition( "accept" )
                        or die "There's no 'accept' transition in the current state";

                my $wi_accepted = $accept->apply( $wi );

                $bug->workflow_instance( $wi_accepted );
        }

Attention nous devons toujours "recharger" l'instance après avoir appliquer une transition. ($bug->workflow_instance( $wi_accepted );)

3. Les restrictions

Avant de continuer dans l'exploration de ce module, nous devons nous arrêter un instant sur le concept de context.

Il s'agit d'un argument qui décrit le contexte appliqué à toutes les transitions. Il pourra fournir des informations sur qui fait la transition, dans quelle condition ... Il sera très utile lors de la mise en oeuvre de restrictions.

Imaginons que nous ayons une classe MyUser :

        package MyUser;

        has id => (
                isa => "Num",
                is  => "ro",
                default => sub { next_unique_id() };
        );

        has name => (
                ...
        );

Nous pouvons alors créer une classe 'context' héritant de Class::Workflow::Context

        package MyWorkflowContext;
        use Moose;

        extends "Class::Workflow::Context";

        has user => (
                isa => "MyUser",
                is  => "rw",
        );

On peut alors appliquer la transition en fournissant le contexte:

        sub accept {
                my ( $bug, $current_user ) = @_;

                my $wi = $bug->workflow_instance;
                my $current_state = $wi->state;

                # if your state supports named transitions      
                my $accept = $current_state->get_transition( "accept" )
                        or croak "There's no 'accept' transition in the current state";

                my $c = MyWorkflowContext->new( user => $current_user );
                my $wi_accepted = $accept->apply( $wi, $c );

                $bug->workflow_instance( $wi_accepted );
        }

La transition a donc accès au contexte $c qui contient notament l'utilisateur courant.

Pour mettre en place les restrictions lors de l'application d'une transition nous allons créer notre propre classe d'instance qui stockera notamment un ' owner ' et un ' submitter ' hériant de la classe MyUser:

        package MyWorkflowInstance;
        use Moose;

        extends "Class::Workflow::Instance::Simple";

        has owner => (
                isa => MyUser",
                is  => "ro", # all instance fields should be read only
        );

        has submitter => (
                isa => MyUser",
                is  => "ro",
        );

Lorsque la première instance est créée l'utilisateur courant est enregistré comme étant ' submitter '.

Nous pouvons enfin définir notre transition en y ajoutant une restriction avec un 'validator' fournit par Class::Workflow::Transition::Validate::Simple :

        $accept->add_validators( sub { 
                                      my ( $self, $instance, $c ) = @_;
                                      die "Not owner" unless $self->instance->owner->id == $c->user->id;
                                     } );

4. La persistence

Jusqu'à maintenant nous ne nous sommes pas soucier du mode de sauvegarde de l'état des bugs. On aurait pû le faire dans un fichier YAML ou XML ou encore en base KiokuDB (base d'objet). Mais ma préférence va vers une sauvegarde en base de donnée, une petite base SQLite faisant très bien l'affaire. Pour cela je me suis inspiré de l'exemple fournit avec les source du module ' example/dbic/t/example_dbic.t '.

J'ai ensuite ajouté quelques champs aux tables du schema Workflow de manière à pouvoir nommé et décrire les workflows, états et transitions. En y aapliquant une couche de Catalyst j'obtiens une interface de gestion de Workflow.

... Affaire à suivre ...


Add_a_comment

Validator_logo
Catapulse v0.06
( 0.0807 s)