2017-09-05 2 views
-1

Supposons que j'ai un module:Comment accéder à un module à partir d'une méthode d'instance dans Ruby?

module M 
    def self.foo 
    ... 
    end 

    def bar 
    ... 
    end 
end 

Module M est inclus dans une classe.

class A 
    include M 
end 

Je veux appeler foo de bar, qui sera éventuellement appelé une instance de A. Quelle est la meilleure façon de le faire à l'intérieur bar?

Bien sûr, je peux juste dire M.foo, mais c'est la duplication du nom du module, ce qui est inutile.

+0

_ « Je veux appeler foo bar » _ - s'il y avait un 'A.foo' en plus de' M.foo' qui 'foo' voudriez-vous appeler? – Stefan

+3

Pourquoi ne voulez-vous pas utiliser 'M.foo'? Cela me semble la chose logique à faire. Il semble étrange d'avoir à la fois la méthode de classe et la méthode d'instance dans le même module, considérant que vous voulez invoquer le premier et inclure le second. –

+1

Il n'y a aucune relation entre une instance de la classe singleton de 'A' et' M'. En particulier, la classe singleton de 'M' n'est pas dans la chaıne d'héritage de' A'. –

Répondre

1

Habituellement, dans un module, il est une bonne pratique d'avoir classe les méthodes et les instances-méthodes distinctes, comme ceci:

module M 
    def bar 
    puts "BAR" 
    self.class.foo 
    end 

    module ClassMethods 
    def foo 
     puts "FOO" 
    end 
    end 
end 

Maintenant, dans une classe, je voudrais idéalement include ce module M d'une manière que j'obtiens A.foo comme méthode de classe et A.new.bar comme méthode d'instance. L'astuce pour cela est Module.included.

module M 
    def bar 
    puts "BAR" 
    self.class.foo 
    end 

    module ClassMethods 
    def foo 
     puts "FOO" 
    end 
    end 

    # when module is included, extend the class with ClassMethods 
    def self.included(base) 
    base.extend ClassMethods 
    end 
end 

class A 
    include M 
end 

A.singleton_methods #=> [:foo] 

A.new.foo 
#=> BAR 
#=> FOO 

Avec cette approche, vous pouvez vous référer à la méthode de classe avec self.class et cela fonctionnera automagiquement.

Espérons que ça aide.

+0

Merci, je n'ai pas pensé à ça! Comme il semble qu'il n'y ait pas de solution qui évite d'inclure 'foo' dans' A', et je comprends pourquoi, grâce au commentaire de Jorg, j'accepte cette réponse comme la plus proche de ce que je veux. –

0

Pas vraiment élégant, mais

def bar 
    Module.nesting.last.foo 
end 

devrait le faire.

Notez que Module#nesting renvoie un tableau car un module peut être imbriqué dans un autre module. Dans le cas général, vous devez appliquer l'index de tableau correct pour choisir le module que vous voulez avoir.

0

Je pense que l'utilisation de M.foo est la meilleure, mais comme un exercice, on pourrait changer M#bar comme suit.

module M 
    def self.foo 
    puts "In self.foo" 
    end 

    def bar 
    puts "In bar" 
    method(__method__).owner.foo 
    end 
end 

class A 
    include M 
end 

a = A.new 
a.bar 
    # In bar 
    # In self.foo 
+0

Envisagez d'utiliser '__caller__' au lieu de' __method__' pour rendre possible l'utilisation de méthodes alias et pour que le code fonctionne toujours. – mudasobwa