Question

Nous utilisons Perl pour l'automatisation des tests d'interface graphique. Il a eu beaucoup de succès. Nous avons écrit un langage DSL très léger pour les tests d'interface graphique. Le DSL est très similaire à un modèle d'objet.

Par exemple, nous avons un objet Application à la racine. Chaque feuille de propriétés de l'application est un objet View. Chaque page sous la page est appelée objet de page lui-même. De Perl, nous envoyons des commandes à une application graphique et l'interface graphique interprète la commande et y répond correctement. Pour envoyer une commande, procédez comme suit:

socket_object->send_command("App.View2.Page2.Activate()")
socket_object->send_command("App.View1.Page3.OKBtn.Click()")

Ce n'est pas très lisible. Au lieu de cela, je veux écrire un DSL Perl pour App, View et Page. Est-ce que Perl fournit une sorte de structure DSL où je peux faire ce qui suit?

App.View2.Page2.Activate();
App.View1.Page2.Click();

Où App doit être une instance de la classe Application. Je dois obtenir l'objet de View2 au moment de l'exécution.

Comment utiliser de telles choses?

Était-ce utile?

La solution

Vous pouvez presque tout faire en Perl. Mais vous devez faire des choses étranges pour que Perl fonctionne avec une syntaxe qui n’est tout simplement pas Perl.

  • Pour gérer exactement ce que vous avez là-bas, il vous faudrait beaucoup d’astuces avancées , qui ne sont par définition pas maintenables. Vous devez:

    • surcharger l'opérateur de concaténation '.' (nécessite une référence bénie)
    • désactiver restrictions ou créer un AUTOLOAD pour autoriser ces mots nus - bien entendu, vous pouvez écrire des sous-mots pour tous les mots que vous souhaitez utiliser (ou utiliser la module barewords .
    • éventuellement, créer plusieurs packages, avec plusieurs AUTOLOAD s
  • Une autre solution consiste à filtres de source , je peux probablement obtenir un vote négatif juste pour mentionner cette capacité. Je ne recommanderais donc pas exactement cette approche aux personnes qui demandent de l'aide . Mais c'est là-bas. Les filtres source (et j'ai fait ma part) ne sont que l'un des domaines dans lesquels vous pouvez penser que vous êtes trop intelligent pour votre propre bien.

    Si vous êtes intéressé par Perl en tant qu'hôte & DSL, langue, puis les filtres sources ne sont pas exactement hors limites. Cependant, en vous limitant à ce que vous voulez montrer, Perl6 :: Attributes fera probablement la plupart de ce dont vous auriez besoin tout de suite. Il prendrait le . et les traduirait en " - > " que Perl comprendrait. Cependant, vous pouvez toujours consulter les filtres source pour comprendre ce qui se passe dans les coulisses.

    Je ne souhaite pas non plus quitter ce sujet sans suggérer qu'une grande partie de la frustration que vous pourriez avoir lors de la génération de votre propre filtre source (ce que je conseille de NE PAS faire) est facilitée en utilisant le Filter :: Simple .

  • Le plus simple est d’abandonner le "." et attendez-vous à la place à un code de type Perl.

    App->View2->Page2->Activate(); 
    App->View1->Page2->Click();
    

    App serait un package ou un sous-programme. Soit défini dans le package actuel, soit importé, qui renvoie un objet associé à un package avec un sous-code View2 (éventuellement un AUTOLOAD sous) qui renvoie le nom d'un package ou une référence associée à un package, qui comprend Page2 , puis enfin, le retour de cela comprendrait Activer ou Cliquez sur . (Voir le didacticiel sur l'utilisation de l'outil hors pair , si vous en avez besoin.)

Autres conseils

Je vous recommande de cesser d'essayer de faire des freaky "DSL". remplissez et écrivez simplement des classes Perl pour gérer les objets que vous souhaitez gérer. Je vous recommande d’envisager l’utilisation du nouveau système d’objets Moose Perl à cette fin, bien que les opérations traditionnelles en Perl OO conviendraient parfaitement. Explorez la documentation Perl pour les didacticiels OO; ils sont géniaux.

Les appels de méthode dans perl5 utilisent - > et non . , ainsi, cela ressemblera à App- > View2- > Page2- > Activer () ou $ App- > View2- > Page2- > Active () à moins que vous ne fassiez quelque chose de vraiment intéressant (par exemple, un filtre source). En supposant que cela soit correct, vous pouvez utiliser des éléments normaux de Perl OO.

Maintenant, la partie suivante de ce dont vous avez besoin est de créer les méthodes au moment de l'exécution. C’est en fait assez simple:

sub _new_view {
    my ($view, $view_num);

    # ...
    # ... (code to create $view object)
    # ...

    my $sym = "App::View$view_num";
    *$sym = sub { return $view }; # can also use Symbol package
}

Alternativement, si vous voulez créer les méthodes uniquement lorsqu'elles sont appelées, c'est ce que AUTOLOAD fait. Vous pouvez également abuser de l'autoload pour assurer le succès de tous les appels de méthode (attention toutefois à ceux qui ont une signification particulière, comme DESTROY).

Cela vous donnera la syntaxe. Demander à vos objets de générer une chaîne à transmettre à send_command ne devrait pas être aussi difficile.

De plus, je ne le connais pas trop bien, mais vous voudrez peut-être consulter Moose . Il existe peut-être des moyens plus simples pour y parvenir.

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

package RemoteAppScript;
use Filter::Simple;    # The basis of many a sane source filter
use Smart::Comments;   # treat yourself and install this if you don't have 
                       # it... or just comment it out.

# Simple test sub
sub send_command { 
    my $cmd = shift;
    print qq(Command "$cmd" sent.\n);
    return;
}

# The list of commands
my @script_list;

# The interface to Filter::Simple's method of source filters.
FILTER { 
    # Save 

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

use RemoteAppScript;
App.View2.Page2.Activate();
App.View1.Page2.Click();

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

App.View2.Page2.Activate();
App.View1.Page2.Click();

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

#!/bin/perl -w 

my $script = do { 
    local $/;
    <ARGV>;
};

$script =~ s/^\s*#.*$//m;

foreach my $command ( 
    grep { length() } map  { s/^\s+|\s+$//g; 

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

package RemoteAppScript;
use Filter::Simple;    # The basis of many a sane source filter
use Smart::Comments;   # treat yourself and install this if you don't have 
                       # it... or just comment it out.

# Simple test sub
sub send_command { 
    my $cmd = shift;
    print qq(Command "$cmd" sent.\n);
    return;
}

# The list of commands
my @script_list;

# The interface to Filter::Simple's method of source filters.
FILTER { 
    # Save 

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

use RemoteAppScript;
App.View2.Page2.Activate();
App.View1.Page2.Click();

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

App.View2.Page2.Activate();
App.View1.Page2.Click();

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

perl run_remote_script.pl remote_app_script.ras

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*>, because Filter::Simple doesn't like you reading more than once. my $mod =

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

<*>

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*>; # v-- Here a Smart::Comment. ### $mod # Allow for whole-line perl style comments in the script $mod =~ s/^\s*#.*$//m; # 1. Break the package up into commands by split # 2. Trim the strings, if needed # 3. lose the entries that are just blank strings. @script_list = grep { length } map { s/^\s+|\s+$//g;

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

<*>

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*> } split /;/, $mod ; ### @script_list # Replace the whole script with a command to run the steps.

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

<*>

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*> = __PACKAGE__ . '::run_script();'; # PBP. return; }; # Here is the sub that performs each action. sub run_script { ### @script_list foreach my $command ( @script_list ) { #send_command( $command ); socket_object->send_command( $command ); } } 1;

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*> } split /;/, $script ) { socket_object->send_command( $command ); }

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*>, because Filter::Simple doesn't like you reading more than once. my $mod =

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

<*>

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*>; # v-- Here a Smart::Comment. ### $mod # Allow for whole-line perl style comments in the script $mod =~ s/^\s*#.*$//m; # 1. Break the package up into commands by split # 2. Trim the strings, if needed # 3. lose the entries that are just blank strings. @script_list = grep { length } map { s/^\s+|\s+$//g;

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

<*>

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*> } split /;/, $mod ; ### @script_list # Replace the whole script with a command to run the steps.

Filtre source DSL

Voici une autre tentative. skiphoppy a un sens, mais au deuxième regard, j’ai remarqué que (jusqu’à présent) vous ne demandiez pas grand chose, c’était aussi complexe. Vous voulez juste prendre chaque commande et dire au serveur distant de le faire. Ce n'est pas perl qui doit comprendre les commandes, c'est le serveur.

J'ai donc retiré certains de mes avertissements concernant les filtres sources et décidé de vous montrer comment un simple peut être écrit. Encore une fois, ce que vous faites n’est pas si complexe, et mon "filtrage" ci-dessous est assez facile.

<*>

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*> = __PACKAGE__ . '::run_script();'; # PBP. return; }; # Here is the sub that performs each action. sub run_script { ### @script_list foreach my $command ( @script_list ) { #send_command( $command ); socket_object->send_command( $command ); } } 1;

Vous devez enregistrer cela dans RemoteAppScript.pm quelque part où votre perl puisse le trouver. (essayez perl -MData :: Dumper -e 'print Dumper (\ @INC), "\ n" " si vous avez besoin de savoir où.)

Ensuite, vous pouvez créer un " perl " fichier qui a ceci:

<*>

Cependant

Il n'y a aucune raison réelle pour que vous ne puissiez pas lire un fichier contenant des commandes de serveur. Cela rejetterait l'appel FILTER . Vous auriez

<*>

dans votre fichier de script, et votre fichier perl ressemblerait davantage à ceci:

<*>

Et appelez ça comme ça:

<*>

http://search.cpan.org/dist/Devel-Declare/ est une alternative moderne aux filtres de source qui s’intègre directement dans l’analyseur perl et mérite un coup d’œil.

Une alternative à la substitution de '.' ou à l'utilisation de la syntaxe - > pourrait être l'utilisation de la syntaxe de package (: :), c.-à-d. la création de packages comme App :: View2 et App :: View2 :: Page2 lors de la création de View2 / Page 2, en ajoutant un sous-conteneur AUTOLOAD au package qui délègue à une méthode App :: View :: Page ou App :: View, quelque chose comme ceci:

Dans votre application / DSL.pm:

package App::DSL;
use strict; 
use warnings;
# use to avoid *{"App::View::$view::method"} = \&sub and friends
use Package::Stash;

sub new_view(%);
our %views;

# use App::DSL (View1 => {attr1 => 'foo', attr2 => 'bar'}); 
sub import {
    my $class = shift;
    my %new_views = @_ or die 'No view specified';

    foreach my $view (keys %new_views) {
            my $stash = Package::Stash->new("App::View::$view");
        # In our AUTOLOAD we create a closure over the right
        # App::View object and call the right method on it
        # for this example I just used _api_\L$method as the
        # internal method name (Activate => _api_activate)
        $stash->add_package_symbol('&AUTOLOAD' =>  
            sub {  
                our $AUTOLOAD;
                my ($method) = 
                   $AUTOLOAD =~ m{App::View::\Q$view\E::(.*)};
                my $api_method = "_api_\L$method";
                die "Invalid method $method on App::View::$view"
                   unless my $view_sub = App::View->can($api_method);
                my $view_obj = $views{$view}
                    or die "Invalid View $view";
                my $sub = sub {
                        $view_obj->$view_sub();
                };
                     # add the function to the package, so that AUTOLOAD
                     # won't need to be called for this method again
                $stash->add_package_symbol("\&$method" => $sub);
                goto $sub;
            });
        $views{$view} = bless $new_views{$view}, 'App::View';
    }
}

package App::View;

# API Method App::View::ViewName::Activate;
sub _api_activate {
    my $self = shift;
    # do something with $self here, which is the view 
    # object created by App::DSL
    warn $self->{attr1};
}

1;

et dans votre script:

use strict;
use warnings;
# Create App::View::View1 and App::View::View2
use App::DSL (View1 => {attr1 => 'hello'}, View2 => {attr1 => 'bye'});
App::View::View1::Activate();
App::View::View2::Activate();
App::View::View1::Activate();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top