2012-09-01 2 views
2

En C, je pourrais générer un exécutable, faire une vaste renommer uniquement refactoring, puis comparer executables pour confirmer que l'exécutable n'a pas changé. Cela a été très pratique pour Assurez-vous que le refactor n'a rien casséComment comparer Rails « » executables » avant et après refactoring?

Est-ce que quelqu'un a fait quelque chose de similaire avec Ruby, en particulier une application Rails? Des stratégies et des méthodes seraient appréciées Idéalement, je pourrais exécuter un script qui produit un seul fichier de quelque sorte Je pense que JRuby ou Rubinus seraient utiles ici

Répondre

2

Je ne pense pas que cette stratégie fonctionnera pour Ruby, contrairement à C, où le compilateur lance En dehors des noms, la plupart des choses que vous nommez dans Ruby portent ce nom avec eux. Cela inclut les classes, les modules, les constantes et les variables d'instance.

unitaires automatisés et des tests d'intégration sont la voie à suivre pour soutenir Ruby refactoring.

+0

Rien ne bat les tests. – DGM

+0

En fait, rien ne vaut de comparer le .exe et de voir qu'ils sont * exactement * les mêmes, garantissant qu'il n'y a rien de différent, plutôt que de faire confiance à la couverture de test ;-). Je suis un fervent partisan de l'écriture de tests, mais je voudrais être capable de faire de simples refactors de renommer une énorme application héritée avec une mauvaise couverture de test et mon chemin est beaucoup plus rapide et certain d'attraper les changements. C'est le but de ma question. –

2

Question intéressante - J'aime la réponse « oui » définitif, vous pouvez obtenir de cette stratégie de régression, au moins pour le cas spécifique de refactoring de renommage.

Je ne suis pas assez expert pour savoir si vous pouvez compiler Ruby (ou au moins un sous-ensemble, sans des choses comme eval), mais il semble y avoir quelques conseils à:

Supposé qu'une approche compilation complète est impossible, qu'en une interprétation abstraite? Vous pouvez analyser le ruby ​​dans un AST, émettre une sorte de code C de l'AST, puis compiler le code C. Le code C n'aurait pas besoin de capturer complètement le comportement du code ruby. Il aurait seulement besoin d'être compilable et d'être distinct chaque fois que le rubis était distinct. (En cours d'exécution En fait, il pourrait donner lieu à du charabia, ou peut-être une erreur de violation de la mémoire immédiate.)

Comme un exemple simple, supposons que la multiplication soutenue rubis et C n'a pas. Ensuite, vous pouvez inclure une fonction mult statique dans votre code C et traduire de: a = b + c*d à a = b + mult(c,d) et le code compilé résultant serait invariant sous le nom refactoring mais montrerait des anomalies sous d'autres sortes de changements. La fonction mult doit pas mettre en œuvre effectivement la multiplication, vous pourriez avoir un de ces à la place:

static int mult(int a, int b) { return a + b; } // pretty close 
static int mult(int a, int b) { return *0; } // not close at all, but still sufficient 

et vous auriez encore obtenir l'invariance dont vous avez besoin aussi longtemps que le compilateur C ne va pas inline la définition. Le même type de traduction, d'une construction ruby ​​incompilable à une construction C moins fonctionnelle mais distincte, devrait fonctionner pour la manipulation d'objet et ainsi de suite, en mappant les opérations de classe en références de structure en C. Le point clé est juste que vous voulez garder les relations de nommage intact tout en sacrifiant le comportement réel.

(Je me demande si vous pourriez faire quelque chose avec une seule structure C qui a des membres (tous des pointeurs vers le même type de structure) nommés après tous les noms de classe et de propriété dans le code ruby. opérations de déréférencement imbriquées utilisant cette structure unique.

Même si vous ne pouvez pas formuler un mappage précis, un mappage imprécis qui manque certaines distinctions mineures peut être suffisant pour augmenter la confiance dans le refactoring de nom d'origine.

Le moyen le plus rapide de mettre en œuvre un tel schéma pourrait être de mapper du code octet à C (plutôt que de l'AST ruby ​​à C). Cela permettrait d'économiser beaucoup d'analyse, mais la cartographie serait plus difficile à comprendre et à vérifier.