2011-07-29 1 views
3

Je voudrais mettre du code dans le module qui génère une erreur si certaines méthodes ne sont pas définies. Ce module repose sur la définition externe de cette méthode, car l'implémentation de cette méthode est différente pour toutes les classes. Ce code aiderait les développeurs à savoir très tôt qu'ils ont oublié d'implémenter la méthode plutôt que d'essayer d'utiliser les fonctionnalités du module.Ruby: Augmenter l'erreur dans le module si la méthode de classe n'est pas trouvée

module MyModule 
    def self.included(klass) 
    raise "MyModule: please `def my_method` on #{klass}" unless klass.respond_to?(:my_method) 
    end 
end 

je peux facilement lever une erreur si une méthode ne se définit pas la définition donnée d'un module, cependant, puisque la plupart des modules sont inclus dans la partie supérieure d'un fichier, il est probable que ma méthode requise est définie dans la classe, mais pas avant que mon module soit inclus.

class MyClass 
    include MyModule 
    def self.my_method 
    # ... 
    end 
end 

Cela soulèverait encore une erreur :(

Est-il possible de soulever une erreur que si la méthode ne se définit pas vraiment dans la définition de classe? Presque besoin d'un rappel class.onload de toutes sortes. Si pas d'autres idées sur la façon d'atténuer les possibilités qu'un programmeur pourrait inclure notre module sans définir cette méthode nécessaire?

Répondre

5

on dirait que vous voulez utiliser method_missing et define_method.

Si vous ne pas utiliser method_missing n'oubliez pas:

  • appel super pour les cas non gérés.
  • également mettre en œuvre une méthode respond_to?

regard sur this question, plus this et that.

Mise à jour:

Il semble l'objectif est de faire comme méthode statique vérification Java ou C++ fait. Ce n'est pas vraiment significatif dans le rubis :-(

Depuis Ruby:

  • Chaque instance d'un objet a ses propres eigenclass Un objet donné peut avoir les méthodes nécessaires mélangées lors de l'exécution Alors juste parce que.. Foo ne dispose pas d'une méthode à temps de chargement de classe n'a pas de sens.
  • cadres comme des crochets RoR method_missing et créer dynamiquement des méthodes nécessaires pour les méthodes d'interrogation de base de données, la méthode peut exister (ou non) quand il est nécessaire.

En ce qui concerne "class on load": Une définition de classe est réellement exécutée. Essayez ceci:

class Foo 
    p "Hi" 
end 

Vous verrez "Hi" le premier et seulement la première fois que Foo est utilisé. C'est ainsi que des choses comme devise s'activent pour faire leur magie.

class User < ActiveRecord::Base 
    # **CALL 'devise' method** 
    devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable 

    # **CALL attr_accessible method** 
    attr_accessible :email, :password, :password_confirmation 
end 

Alors peut-être par convention privée ont les développeurs ajouter un appel de méthode check_class au fond des classes en question?Je comprends l'intention, mais il semble que ce soit combattre la façon dont le rubis est conçu pour fonctionner.

En tant que personne principalement Java j'apprécie la frustration. Laissez-moi deviner: des cas répétés de code poussés à la production qui avait des méthodes manquantes? :-P

Update2:

WRT onload En utilisation sauf rubis d'une classe de nouvelles méthodes se définit gelé tout le temps. (Ou une instance peut obtenir de nouvelles méthodes définies uniquement pour cette instance.) Vérifier ainsi l'absence de méthode d'une méthode est seulement une vérification d'instantané et non une vérification définitive comme un langage statique apporte à la table. Il s'agit de la méthode de ruby ​​Halting problem

+1

Je suis familier avec les classes propres, et oui j'essaye de faire quelque chose comme la vérification de méthode statique. Un de mes collègues a souligné que nous pourrions faire passer cette attente dans les tests pour les modèles implémentant MyModule. Nous pourrions avoir une macro acts_as_my_module que nous pouvons mixer avec tous les modèles/specs. Toujours pas se plaindre aussi fort, et nécessite un dévouement de programmeur supplémentaire, mais est le plus ruby-esque – Schneems

+0

Si vous utilisez rspec, vous pouvez faire quelque chose comme ça http://relishapp.com/rspec/rspec-core/v/ 2-6/dir/example-groups/shared-examples – Schneems

+0

Tout bien considéré, vous voulez probablement un outil qui s'exécute dans le cadre de votre déploiement et arrête le déploiement s'il y a un problème comme celui-ci. Il semble que vous le sachiez déjà, mais espérais une alternative :-) – Pat

3

Pourquoi ne pas déclarer une méthode avec ce nom, ce qui provoque simplement une erreur, pour s'assurer que l'utilisateur redéfinit la méthode?

module MyModule 
    def my_method 
    raise "Please implement me" 
    end 
end 


class MyClass 
    include MyModule 
    def my_method 
    # do something 
    end 
end 
+0

J'ai considéré ceci, bien que vous ne sentiez toujours pas la douleur jusqu'à ce que vous essayiez d'appeler la méthode. – Schneems

+0

Si vous omettez la définition, elle aura à peu près le même effet. En fait, il sera même * meilleur *, car au lieu d'élever une exception générique non RuntimeError, il déclenchera une exception 'NoMethodError', ce qui vous indique à peu près tout ce que vous devez savoir: une méthode qui était attendue être là, n'était pas. C'est la manière idiomatique de Ruby et c'est par exemple la façon dont 'Enumerable' et' Comparable' le font. –

2

En supposant que votre programme exige que tous les fichiers lors du démarrage et ne pas utiliser de autoload et autres, vous pouvez utiliser quelque chose comme ce qui suit juste après tout est nécessaire, mais avant que le programme commence réellement:

classes_to_check = Object.constants.find_all do |const| 
    klass = Object.const_get(c) 
    klass.ancestors.include?(MyModule) if klass.kind_of?(Module) 
end 

classes_to_check.each do |klass| 
    raise "MyModule: please `def my_method` on #{klass}" \ 
    unless klass.respond_to?(:my_method) 
end 

Cependant, personnellement, j'utilise toujours la solution de Dogbert.

Questions connexes