2010-11-09 5 views
5

Disons que j'ai deux modules. Est-il possible d'inclure un module dans un autre qui se comporterait comme un mixin?Comment faire pour que les mixins de modules fonctionnent pour des méthodes statiques?

Par exemple:

module A 
    def self.foo 
    puts "foo" 
    bar 
    end 
end 

module B 
    include A 
    def self.bar 
    puts "bar" 
    end 
end 

B.bar 
B.foo 

Edit: je réalisais que je copiais à l'origine du code vers le bas mauvais. Les méthodes doivent être statiques. Le code corrigé est ci-dessus (et ne fonctionne pas).

+0

Les modules sont des classes (Module.class == Classe) de sorte qu'il a été répondu, oui. –

Répondre

9

Utilisez extend à la place de include pour ajouter des méthodes de classe.

module A 
    module ClassMethods 
    def foo 
     puts "foo" 
     puts bar 
    end  
    end 
    extend ClassMethods  
end 

module B 
    extend A::ClassMethods 
    def self.bar 
    puts "bar" 
    end 
end 

B.bar 
B.foo 
-3

Le code exact que vous avez affiché fonctionne exactement comme vous le souhaitez. Donc, la réponse est oui.

Aurait-il été si difficile de l'exécuter vous-même?

+0

+1 Peut-être at-il seulement un téléimprimeur et il affiche ce code du passé quand les parseurs Ruby n'ont même pas encore été inventés. C'est ma conjecture au moins. –

+0

@Mike har har ... – juan2raid

+0

@Mike, alors c'est triste que nous ne bougions pas au moins aussi vite que nous pourrions rester dans le futur. –

18

Comme vous l'avez appris, cela ne fonctionne pas, mais pourquoi cela ne fonctionne pas est une très bonne leçon sur le modèle d'objet Ruby. Lorsque vous créez une instance d'un objet, ce que vous avez créé est un nouvel objet avec un ensemble de variables d'instance et un pointeur vers la classe de l'objet (et quelques autres choses comme un ID d'objet et un pointeur vers le superclasse) mais les méthodes elles-mêmes ne sont pas dans l'instance de l'objet. La définition de classe contient la liste des méthodes et leur code (et un pointeur vers sa propre classe, un pointeur vers sa superclasse et un ID d'objet). Lorsque vous appelez une méthode sur une instance Ruby recherche la classe de l'instance et recherche dans la liste de méthodes de cette classe la méthode que vous avez appelée. S'il ne le trouve pas alors il regarde dans la superclasse de la classe. S'il ne le trouve pas, il regarde dans la superclasse de cette classe jusqu'à ce qu'il n'y ait plus de superclasses. Puis il retourne à la première classe et cherche une méthode method_missing. S'il n'en trouve pas, il passe à la superclasse et ainsi de suite jusqu'à ce qu'il atteigne l'objet racine où il est conçu pour générer une erreur.

Disons par exemple, vous avez une personne de classe et vous faites une instance de la classe avec la bubba variables comme ceci:

class Person 
    attr_accessor :dob, :name 
    def age 
    years = Time.now.year - @dob.year 
    puts "You are #{years} year#{"s" if years != 1} old" 
    end 
    def feed 
    puts "nom, nom, nom" 
    end 
end 
bubba = Person.new 
bubba.name = "Bubba" 
bubba.dob = Time.new(1983,9,26) 

Le diagramme de classe ressemblerait à quelque chose comme ceci: alt text

Alors que se passe-t-il lorsque vous créez une méthode statique, une méthode de classe/module? Eh bien, rappelez-vous que presque tout est un objet dans Ruby et une définition de module est une instance de la classe Class. Oui, ce code que vous tapez est en fait une instance aussi, c'est du code en direct. Lorsque vous créez une méthode de classe en utilisant def self.method_name, vous créez une méthode dans l'instance de l'objet qui est la définition de classe/module.

Très bien, alors où cette méthode de classe est-elle définie? Il est défini dans une classe anonyme (aka singleton, eigen, ghost class) qui est créée exactement pour cette raison.

Pour en revenir à notre classe Person si l'on ajoute une méthode de classe sur la bubba instance comme ceci:

def bubba.drive_pickup 
    puts "Yee-haw!" 
end 

Cette méthode est mis dans une classe spéciale singleton créé juste pour cette instance et la superclasse du singleton est maintenant la classe Person. Cela fait ressembler notre chaîne d'appel de méthode à ceci: alt text

Toutes les autres méthodes définies sur l'objet instance bubba seront également placées dans cette classe singleton. Il n'y a jamais plus d'une classe singleton par objet d'instance.Donc, pour envelopper tout cela, la raison pour laquelle cela ne marche pas est que les méthodes statiques des modules sont définies dans la classe singleton pour l'instance de la définition du module. Lorsque vous incluez ou étendez à partir du module, vous ajoutez un pointeur à la table de méthodes du module mais pas à la table de méthodes de l'objet d'instance de la classe singleton pour le module. Pensez-y de cette façon: Si vous créez une instance x de type Z et une instance y de type Z devrais-je savoir sur y? Non, pas à moins d'en avoir été spécifiquement informé. De même, votre module qui mixe dans un autre module ne devrait pas connaître un autre objet qui a justement ce premier module comme sa super-classe.

Pour une meilleure explication du modèle objet Ruby regarder cette vidéo gratuite impressionnante par le étonnamment érudite Dave Thomas (non, pas le gars de Wendy):
http://scotland-on-rails.s3.amazonaws.com/2A04_DaveThomas-SOR.mp4

Après avoir regardé cette vidéo je l'ai acheté l'ensemble de Dave Thomas series on the Ruby object model de Pragmatic et ça valait vraiment le coup.

P.S. N'importe qui s'il vous plaît n'hésitez pas à me corriger sur tout ce que j'ai oublié; comme ce qui est spécifiquement dans un objet.

Questions connexes