2010-07-30 4 views
2

Im lecture Métaprogrammation en Ruby.Ruby define_method question

Voici deux extraits de code du livre:

my_var = "Success" 

MyClass = Class.new do 
    puts "#{my_var} in the class definition!" 
    define_method :my_method do 
    puts "#{my_var} in the method!" 
    end 
end 

MyClass.new.my_method 

⇒ Success in the class definition! 
    Success in the method! 

et:

def define_methods 
    shared = 0 

    Kernel.send :define_method, :counter do 
    shared 
    end 

    Kernel.send :define_method, :inc do |x| 
    shared += x 
    end 
end 

define_methods 

counter # => 0 
inc(4) 
counter # => 4 

Je me demande pourquoi on n'a pas à utiliser l'envoi dynamique (utilisation de Kernel.send) lors de la définition de la méthode dans le premier exemple alors que l'on doit l'utiliser dans le deuxième exemple. Je lui ai donné quelques idées mais je ne peux pas le comprendre.

Merci!

Répondre

4

Module#define_method est privé. Les méthodes privées peuvent uniquement être appelées sans récepteur. Ainsi, dans le second exemple, vous devez utiliser la réflexion (c'est-à-dire send) pour contourner les restrictions d'accès.

Notez que le premier exemple utilise encore l'envoi dynamique. En fait, dans Ruby, tout (sauf les accès variables et certains opérateurs intégrés) utilise la répartition dynamique.

+0

"Les méthodes privées peuvent uniquement être appelées sans récepteur." - donc ... omettez simplement le récepteur, alors ça va être un récepteur implicite, et ça marche bien. vois ma réponse. ai-je mal compris quelque chose? –

+0

@KarolyHorvath: le récepteur implicite est 'self', mais l'auteur veut que le récepteur soit' Kernel'. –

0

Contrairement à ce que dit le livre, cela fonctionne sans Kernel.send, vous avez juste besoin d'un contexte où la classe self inclut Kernel - ce qui est presque toujours le cas.

par exemple, si vous faites cela dans le champ d'application principal, il est là:

irb(main):024:0> self.class.ancestors 
=> [Object, Kernel, BasicObject] 

Ainsi, en utilisant

define_method :counter do 
    shared 
    end 

fonctionne très bien.

Vous avez seulement besoin de cette astuce si, dans le contexte actuel, Kernel n'est pas inclus, par ex. si vous êtes à l'intérieur d'une plaine BasicObject, ou une classe descendante créée par l'utilisateur.