Dans ruby vous utilisez le point pour appeler une méthode, ou en d'autres termes, pour envoyer une méthode à l'objet traité.
Traceback (most recent call last):
2: from /Users/albert/.rbenv/versions/2.5.0/bin/irb:11:in `<main>'
1: from (irb):17
NoMethodError (private method `puts' called for main:Object)
Nous envoyons à l'objet "100" le message to_i
Quand on fait:
self.puts "hello"
La méthode met, à quel objet est appliqué?
J'ai pensé ceci: p>
puts "hello"
Là où soi serait principal, comme l'environnement dans lequel vous êtes maintenant, la console irb par exemple.
Mais cela génère une erreur.
"100".to_i
Pourquoi est-ce faux? Où la méthode est-elle appliquée?
MISE À JOUR: Ruby 2.7 permet d'appeler des méthodes privées avec self. Cette fonctionnalité a été demandée et discutée sur 1 et 2 .
3 Réponses :
met a> est une méthode de module du Kernel . Le Kernel est inclus par Object et est donc disponible dans presque toutes les classes. met "foo" et Kernel.puts "foo" sont équivalents. La différence est que Kernel.puts est explicite tandis que met pourrait appeler une méthode met définie localement.
def puts(str)
p "my puts: #{str}"
end
puts "foo"; # "my puts: foo"
Kernel.puts "foo" # foo
Sous le capot, Kernel.puts appelle $ stdout.puts . $ stdout est une instance globale prédéfinie de IO .
L'OP a raison de penser que, lorsque le récepteur explicite est omis, le récepteur devient self . Et comme met fonctionne sans récepteur explicite, ce n'est pas si mal qu'une idée d'essayer self.puts "hello" dans le même environnement. En effet, met est défini sur main , ou tout autre objet vers lequel self pointe dans l'environnement donné.
Le problème ici est que les méthodes peuvent être appelées avec un récepteur explicite sont des méthodes publiques , alors que la méthode met est une méthode privée , qui rejette les récepteurs explicites.
Une manière standard de contourner cette restriction est d'utiliser la méthode send comme suit:
self.send(:puts, "hello")
Ah! Je m'étais demandé si Ruby faisait une exception pour les méthodes Kernel, mais ce serait comme un-Ruby. Ceci explique pourquoi Kernel est inclus dans Object. Un test intéressant est d'ajouter une méthode privée au noyau pour inspecter le récepteur comme module Kernel private def whoami self.class; fin; fin; p whoami ()
@Schwern Si vous voulez voir où met est défini, vous pouvez simplement faire method (: putting) .owner # => Kernel .
Ouais, c'était clair où c'était défini. Je ne savais pas qui était le destinataire ni comment une méthode privée était appelée. Bien que je ne sache toujours pas comment self est une instance d'Object en dehors d'une définition de méthode. Il s'agit généralement d'une instance de Class. Juste un cas spécial?
@Schwern Oui, c'est un cas particulier. Dans l'environnement principal, self est l'objet appelé main .
Ce n'est qu'une instance de Class à l'intérieur de class ... end . C'est une instance de l'instance Module de module ... end . C'est une instance de la méthode en cours de définition à l'intérieur de def ... end . En dehors de tout, c'est main . Ce n'est pas une exception, vraiment - c'est juste qu'il y a très peu de fois que quelqu'un essaie explicitement d'examiner self en dehors de l'un des blocs mentionnés ci-dessus.
Dans Ruby, lorsque vous omettez le destinataire d'un message envoyé, le destinataire implicite est toujours self . (Aucune exception.)
Donc, dire
self.puts
où toto n'est pas une variable locale dans la portée lexicale actuelle, est toujours équivalent à
puts
Cependant, il y a une petite ride: en Ruby, la définition d'une méthode private est "une méthode qui ne peut être invoqué qu'à la suite d'un message envoyé sans récepteur ". Ainsi, alors que foo et self.foo sont équivalents en termes de destinataire du message envoyé, il peut y avoir des différences avec le contrôle d'accès. En particulier, si foo est private , alors foo fonctionnera mais self.foo augmentera une exception NoMethodError avec le message (par exemple) NoMethodError (méthode privée `foo 'appelée pour main: Object) .
Donc, vous aviez raison depuis le début:
self.foo
équivaut à
foo
mais vous avez mal lu le message d'erreur: ce n'est pas que le La méthode n'existe pas, c'est que la méthode est private.
En particulier, toutes ces méthodes qui sont censées ressembler davantage à des "procédures" en ce qu'elles ne le font pas font réellement quelque chose d'intéressant avec leur récepteur, sont définis dans Kernel et sont définis comme des méthodes privées . Cela inclut également des méthodes telles que Kernel # print , Kernel # require , etc.
Vous dites que "ça jette une erreur". Avez-vous lu l'erreur? Que vous dit l'erreur?