jeudi 13 juin 2013

[Ruby] duel de méthodes : send vs instance_eval

Ces derniers jours je me suis retrouvé confronté aux méthodes #send et #instance_eval d'une instance d'un objet ruby quelquonque.

A priori ces méthodes font la même chose : elles exécutent du code passé en paramètre.

Pour la suite de l'article, nous utiliserons la classe suivante :

class Jedi

   def initialize
       @action = "t'harponner"
   end

   def slice
       "J'utilise mon sabre laser, écarte toi !!"
   end

   def use_force action
      "Je vais te #{action} avec la force!"
   end

end
Pour #send, il s'agit de passer le nom du message sous forme de symbole ou chaîne de caractère, suivi des paramètres.
Jedi.new.send :slice   #=> "J'utilise mon sable laser, écarte toi !!"

Jedi.new.send :use_force, 'persuader'   #=> "Je vais te persuader avec la force!"
Pour #instance_eval, il s'agit de passer du code à évaluer avec une chaîne de caractère ou un bloc.
Jedi.new.instance_eval 'slice'   #=>"J'utilise mon sable laser, écarte toi !!"

Jedi.new.instance_eval ' use_force "persuader" '   #=> "Je vais te persuader avec la force!"

Jedi.new.instance_eval { use_force "persuader" }   #=> "Je vais te persuader avec la force!"
La différence entre les deux méthodes vient de leur portée.

#intance_eval exécute le code dans le contexte de l'objet recevant la méthode #instance_eval.

Alors que #send exécute le message dans le contexte dans lequel il est appelé.

Des exemples sont bien plus parlants :
  irb > jedi = Jedi.new
  irb > @action = "Dark Vador-iser"

  irb > jedi.send :use_force, @action
  => "Je vais te Dark Vador-iser avec la force!"

  irb > jedi.instance_eval { use_force @action }
   => "Je vais te t'harponner avec la force!"
Alors convaincu ?!


Et merci à Greg pour avoir vérifier tout ça dans irb :)

Aucun commentaire: