web-dev-qa-db-fra.com

Comprendre les méthodes privées dans Ruby

class Example
 private
 def example_test
  puts 'Hello'
 end
end

e = Example.new
e.example_test

Bien sûr, cela ne fonctionnera pas, car nous avons spécifié l'instance de récepteur explicite de l'exemple (e), et c'est contre une "règle privée".

Mais je ne peux pas comprendre pourquoi on ne peut pas faire dans Ruby ceci:

class Foo
 def public_m
  self.private_m # <=
 end
 private
 def private_m
  puts 'Hello'
 end
end

Foo.new.public_m

L'objet actuel à l'intérieur de public_m la définition de méthode (c'est-à-dire self) est l'instance de Foo. Alors pourquoi ce n'est pas permis? Pour résoudre ce problème, je dois changer self.private_m pour juste private_m. Mais pourquoi cela diffère, le self n'est-il pas une instance de Foo dans public_m? Et qui est le destinataire de la parole nue private_m appel? N'est-ce pas self - qu'est-ce que vous omettez réellement parce que, Ruby le fera pour vous (appellera private_m sur vous-même)?

J'espère que je ne l'ai pas trop confondu, je suis encore frais pour Ruby.


EDIT: Merci pour toutes les réponses. En les rassemblant, j'ai (enfin) pu voir l'évidence (et pas si évident pour quelqu'un qui n'a jamais vu des choses comme Ruby): que self lui-même peut être un récepteur explicite et implicite et qui fait la différence . Il y a donc deux règles, si vous voulez appeler une méthode privée: self doit être un récepteur implicite, et ce self doit être une instance de la classe actuelle (Example dans ce cas - et cela prend placer uniquement lorsque self si à l'intérieur de la définition de méthode d'instance, pendant l'exécution de cette méthode). S'il vous plait corrigez moi si je me trompe.

class Example 

 # self as an explicit receiver (will throw an error)
 def explicit 
  self.some_private_method
 end

 # self as an implicit receiver (will be ok)
 def implicit
  some_private_method
 end

 private

 def some_private_method; end
end

Example.new.implicit

Message pour tous ceux qui ont pu trouver cette question pendant les parcours Google: cela peut être utile - http://weblog.jamisbuck.org/2007/2/23/method-visibility-in-Ruby

49
Ernest

Voici le court et le long. Ce que signifie privé dans Ruby est une méthode ne peut pas être appelée avec des récepteurs explicites, par exemple some_instance.private_method (value). Donc, même si le récepteur implicite est self, dans votre exemple, vous utilisez explicitement self so les méthodes privées ne sont pas accessibles.

Pensez-y de cette façon, vous attendriez-vous à pouvoir appeler une méthode privée en utilisant une variable que vous avez affectée à une instance d'une classe? Non. Self est une variable et doit donc suivre les mêmes règles. Cependant, lorsque vous appelez simplement la méthode à l'intérieur de l'instance, cela fonctionne comme prévu, car vous ne déclarez pas explicitement le récepteur.

Ruby étant ce que c'est, vous pouvez réellement appeler des méthodes privées en utilisant instance_eval:

class Foo
  private
  def bar(value)
    puts "value = #{value}"
  end
end

f = Foo.new
begin
  f.bar("This won't work")
rescue Exception=>e
  puts "That didn't work: #{e}"
end
f.instance_eval{ bar("But this does") }

J'espère que c'est un peu plus clair.

-- Éditer --

Je suppose que vous saviez que cela fonctionnera:

class Foo
 def public_m
  private_m # Removed self.
 end
 private
 def private_m
  puts 'Hello'
 end
end

Foo.new.public_m
51
Mike Bethany

definition of private in Ruby is "can only be called without an explicit receiver". Et c'est pourquoi vous ne pouvez appeler que des méthodes privées sans récepteur explicite. Il n'y a pas d'autre explication.

Notez qu'il existe en fait une exception à la règle: en raison de l'ambiguïté entre les variables locales et les appels de méthode, ce qui suit toujours sera résolu pour être une affectation à une variable locale:

foo = :bar

Alors, que faites-vous si vous voulez appeler un écrivain appelé foo=? Eh bien, vous avez pour ajouter un récepteur explicite, car sans le récepteur Ruby ne saura tout simplement pas que vous voulez appeler la méthode foo= au lieu de l'assigner à la variable locale foo:

self.foo = :bar

Mais que faites-vous si vous voulez appeler un écrivain private appelé foo=? Vous ne pouvez pas écrire self.foo = car foo= est private et ne peut donc pas être appelé avec un récepteur explicite. Eh bien, en fait pour ce cas spécifique (et ce cas seul), vous pouvez utilisez en fait un récepteur explicite de self pour appeler un rédacteur private.

17
Jörg W Mittag

C'est bizarre, mais beaucoup de choses sur les modificateurs de visibilité de Ruby sont bizarres. Même si self est le récepteur implicite, l'épeler le rend explicite aux yeux du runtime Ruby. Quand il dit que les méthodes privées ne peuvent pas être appelées avec un récepteur explicite , c'est ce que cela signifie, même self compte.

14
Theo

IIRC, les méthodes privées permettent seulement récepteur implicite (qui est toujours soi, bien sûr).

3
Victor Deryagin

Ajout de quelques améliorations à la solution User Gates. L'appel d'une méthode privée à une méthode de classe ou une méthode d'instance est à peu près possible. Voici les extraits de code. Mais non recommandé.

Méthode de classe

class Example
  def public_m
    Example.new.send(:private_m)
  end

  private
  def private_m
    puts 'Hello'
  end
end

e = Example.new.public_m

Méthode d'instance

class Example
  def self.public_m
    Example.new.send(:private_m)
  end

  private
  def private_m
    puts 'Hello'
  end
end

e = Example.public_m
1
Dinesh Pallapa

Désolé pour ma réponse précédente. Je ne comprends tout simplement pas votre question.

J'ai changé votre code comme ceci:

class Foo
 def public_m
  private_m # <=
 end

 def Foo.static_m
   puts "static"
 end

 def self.static2_m
   puts "static 2"
 end

 private 
 def private_m
  puts 'Hello'
 end
end

Foo.new.public_m
Foo.static_m
Foo.static2_m

Voici un appel de méthode d'instance:

 def public_m
  private_m # <=
 end

Voici un appel de méthodes de classe:

 def Foo.static_m
   puts "static"
 end

 def self.static2_m
   puts "static 2"
 end

Foo.static_m
Foo.static2_m
1
demas

Ne répond pas exactement à la question, mais vous pouvez appeler des méthodes privées de cette façon

class Example
 private
 def example_test
  puts 'Hello'
 end
end

e = Example.new
e.send(:example_test)
0
gates