class << self
est plus qu'un simple moyen de déclarer des méthodes de classe (bien qu'il puisse être utilisé de cette façon). Vous avez probablement déjà vu des utilisations comme :
class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end
Cela fonctionne, et est équivalent à def Foo.a
mais la façon dont cela fonctionne est un peu plus subtile. Le secret est que self
dans ce contexte, fait référence à l'objet Foo
dont la classe est une sous-classe unique et anonyme de Class
. Cette sous-classe est appelée Foo
's Classe propre . Donc def a
crée une nouvelle méthode appelée a dans Foo
accessible par la syntaxe normale d'appel de méthode : Foo.a
.
Voyons maintenant un autre exemple :
str = "abc"
other_str = "def"
class << str
def frob
return self + "d"
end
end
print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str
Cet exemple est le même que le précédent, bien que cela puisse être difficile à voir au premier abord. frob
est défini, et non sur le String
mais sur la classe propre de str
une sous-classe anonyme unique de String
. Donc str
a un frob
mais les instances de la méthode String
en général ne le font pas. Nous pourrions également avoir des méthodes surchargées de String (très utile dans certains scénarios de test délicats).
Maintenant, nous sommes équipés pour comprendre votre exemple original. A l'intérieur de Foo
de la méthode d'initialisation, self
ne fait pas référence à la classe Foo
mais à un certain niveau instance de Foo
. Sa classe propre est une sous-classe de Foo
mais ce n'est pas le cas Foo
; ce n'est pas possible, sinon l'astuce que nous avons vue dans le deuxième exemple ne pourrait pas fonctionner. Donc pour continuer votre exemple :
f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)
f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.
J'espère que cela vous aidera.