2008-08-21 6 views
14

Autre que self.class.send :method, args..., bien sûr. Je voudrais rendre une méthode plutôt complexe disponible au niveau de la classe et de l'instance sans dupliquer le code.Existe-t-il un moyen d'appeler une méthode Class privée à partir d'une instance dans Ruby?


MISE À JOUR:

Branam @ Jonathan: c'était mon hypothèse, mais je voulais vous assurer que personne d'autre avait trouvé un moyen de contourner. La visibilité dans Ruby est très différente de celle de Java. Vous êtes également tout à fait raison que private ne fonctionne pas sur les méthodes de classe, bien que cela déclarera une méthode de classe privée:

class Foo 
    class <<self 
    private 
    def bar 
     puts 'bar' 
    end 
    end 
end 

Foo.bar 
# => NoMethodError: private method 'bar' called for Foo:Class 

Répondre

11

Voici un extrait de code pour aller de pair avec la question. L'utilisation de "private" dans une définition de classe ne s'applique pas aux méthodes de classe. Vous devez utiliser "private_class_method" comme dans l'exemple suivant.

class Foo 
    def self.private_bar 
    # Complex logic goes here 
    puts "hi" 
    end 
    private_class_method :private_bar 
    class <<self 
    private 
    def another_private_bar 
     puts "bar" 
    end 
    end 
    public 
    def instance_bar 
    self.class.private_bar 
    end 
    def instance_bar2 
    self.class.another_private_bar 
    end 
end 

f=Foo.new 
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class 
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class 

Je ne vois pas un moyen de contourner ce problème. La documentation indique que vous ne pouvez pas spécifier la réception d'une méthode privée. En outre, vous pouvez uniquement accéder à une méthode privée à partir de la même instance. La classe Foo est un objet différent d'une instance donnée de Foo.

Ne prenez pas ma réponse comme finale. Je ne suis certainement pas un expert, mais je voulais fournir un extrait de code afin que les autres qui tentent de répondre auront des méthodes de classe privée.

-1

À moins que je suis malentendu, ne vous avez besoin juste quelque chose comme ceci:

class Foo 
    private 
    def Foo.bar 
     # Complex logic goes here 
     puts "hi" 
    end 

    public 
    def bar 
     Foo.bar 
    end 
end 

Bien sûr, vous pouvez changer la deuxième définition pour utiliser votre approche self.class.send si vous vouliez éviter hardcoding le nom de classe ...

0

Si votre méthode est simplement un utility function (qui est, il ne repose pas sur des variables d'instance), vous pouvez mettre la méthode dans un module et include et extend la classe afin qu'il soit disponible à la fois une méthode de classe privée et une méthode d'instance privée.

0

C'est la façon de jouer avec les méthodes «réelles» de classe privée.

class Foo 
    def self.private_bar 
    # Complex logic goes here 
    puts "hi" 
    end 
    private_class_method :private_bar 
    class <<self 
    private 
    def another_private_bar 
     puts "bar" 
    end 
    end 
    public 
    def instance_bar 
    self.class.private_bar 
    end 
    def instance_bar2 
    self.class.another_private_bar 
    end 
    def calling_private_method 
    Foo.send :another_private_bar 
    self.class.send :private_bar 
    end 
end 
f=Foo.new 
f.send :calling_private_method 
# "bar" 
# "hi" 
Foo.send :another_private_bar 
# "bar" 

acclamations

+0

Obtention d'une erreur pour le premier exemple: '1.9.3p327: 078> f = Foo.new => # 1.9.3p327: 079> f.class.send: calling_private_method NoMethodError: méthode définie \ 'calling_private_method » pour Foo: Classe de (RIR) : 79 de ~/.rvm/rubies/ruby-1.9.3-p327/bin/irb: 16: in \ '

'' – TrinitronX

+0

Oui, cela ne fonctionne pas (ou ne fonctionne peut-être plus). –

+0

Il y avait une faute de frappe - mis à jour mon code @JacobCrofts – metakungfu

3

Permettez-moi de contribuer à cette liste de solutions et non-solutions plus ou moins étranges:

puts RUBY_VERSION # => 2.1.2 

class C 
    class << self 
    private def foo 
     'Je suis foo' 
    end 
    end 

    private define_method :foo, &method(:foo) 

    def bar 
    foo 
    end 
end 

puts C.new.bar # => Je suis foo 
puts C.new.foo # => NoMethodError 
+1

Comme la demande de publication d'origine, il doit * appeler * la méthode de classe. Ce n'est pas bon car c'est la copie de la méthode dans l'ensemble des méthodes d'instance de la classe. Si vous, par exemple, monke patch la méthode de classe C :: foo, vous auriez soudainement un comportement incompatible entre le niveau de classe :: foo et le niveau d'instance #foo. –

+0

Je pense que vous avez raison, merci. – Alexey

0

De nos jours, vous ne avez pas besoin des méthodes d'aide plus. Vous pouvez simplement les intégrer avec votre définition de méthode. Cela devrait se sentir très familier aux gens Java:

class MyClass 

    private_class_method def self.my_private_method 
    puts "private class method" 
    end 

    private def my_private_method 
    puts "private instance method" 
    end 

end 

Et non, vous ne pouvez pas appeler une méthode de classe privée d'une méthode d'instance. Toutefois, vous pouvez implémenter la méthode de classe privée comme méthode de classe publique dans une classe imbriquée privée à la place, en utilisant la méthode d'assistance private_constant. Voir this blogpost pour plus de détails.

Questions connexes