Ruby - Utiliser les blocs pour faire du refactoring - partie 2
Suite à l’article d’hier
(Utiliser les blocs pour faire du refactoring),
on m’a demandé la différence entre block.call
et yield
. C’est parti.
On s’était arrêté là:
class Bidule
def un
helper('un') { puts 'Au milieu de la méthode un' }
end
def deux
helper('deux') do
puts 'Ceci est le milieu de la méthode deux'
end
end
private
def helper(argument, &block)
puts "Début de la méthode #{argument}"
block.call
puts "Fin de la méthode #{argument}"
end
end
bidule = Bidule.new
bidule.un
bidule.deux
Essayons de remplacer block.call
par yield
:
class Bidule
def un
helper('un') { puts 'Au milieu de la méthode un' }
end
def deux
helper('deux') do
puts 'Ceci est le milieu de la méthode deux'
end
end
private
def helper(argument, &block)
puts "Début de la méthode #{argument}"
yield
puts "Fin de la méthode #{argument}"
end
end
bidule = Bidule.new
bidule.un
bidule.deux
Lorsqu’on lance le programme, on voit qu’il n’y a pas de différences:
$ ruby test.rb
Début de la méthode un
Au milieu de la méthode un
Fin de la méthode un
Début de la méthode deux
Ceci est le milieu de la méthode deux
Fin de la méthode deux
Ok, donc block.call
et yield
c’est pareil ? Attends encore. Essayons
maintenant de supprimer le &block
:
class Bidule
def un
helper('un') { puts 'Au milieu de la méthode un' }
end
def deux
helper('deux') do
puts 'Ceci est le milieu de la méthode deux'
end
end
private
def helper(argument)
puts "Début de la méthode #{argument}"
yield
puts "Fin de la méthode #{argument}"
end
end
bidule = Bidule.new
bidule.un
bidule.deux
Toujours pas de différences ! Par contre, on ne pourra pas appeler
block.call
sans avoir défini &block
:
def helper(argument)
puts "Début de la méthode #{argument}"
block.call
puts "Fin de la méthode #{argument}"
end
Le code ci-dessus donnera évidemment une erreur:
$ ruby test.rb
Début de la méthode un
test.rb:16:in `helper': undefined local variable or method `block' for
#<Bidule:0x9eaf6ec> (NameError)
Toutes ces expérimentations nous ammène à une première conclusion: Les blocs sont implicites, et donc ils sont partout. Ce que confirme, s’il en est encore besoin, la session irb suivante:
>> def foo(arg)
>> puts arg
>> end
=> :foo
>> foo('ok') { puts 'I am in a block' }
ok
Le contenu du bloc n’est jamais évalué, mais ne provoque pas d’erreur
lors de l’appel de foo
.
Seconde conclusion, block.call
et yield
fonctionnent à l’identique.
Bien que je préfère block.call
, qui me force à documenter la méthode
avec le &block
.
À demain.