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 JediPour #send, il s'agit de passer le nom du message sous forme de symbole ou chaîne de caractère, suivi des paramètres.
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
Jedi.new.send :slice #=> "J'utilise mon sable laser, écarte toi !!"Pour #instance_eval, il s'agit de passer du code à évaluer avec une chaîne de caractère ou un bloc.
Jedi.new.send :use_force, 'persuader' #=> "Je vais te persuader avec la force!"
Jedi.new.instance_eval 'slice' #=>"J'utilise mon sable laser, écarte toi !!"La différence entre les deux méthodes vient de leur portée.
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!"
#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.newAlors convaincu ?!
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!"
Et merci à Greg pour avoir vérifier tout ça dans irb :)