Ruby 2.5 introduit une nouvelle méthode du plus haut intérêt : Object#yield_self
.
En voici une version (grossièrement) simplifiée :
class Object def yield_self yield(self) end end
Au premier coup d'œil, ceci ne passe pas pour une fonctionnalité remarquable. Cependant, cette fonctionnalité est très similaire à l'opérateur Pipe d'Elixir ou de F#. Cette fonctionnalité peut sembler similaire à la méthode Object#tap
mais le retour de ces méthodes diffère.
Différence avec Object#tap
Commençons par une simple classe d'exemple (Product
) :
class Product attr_accessor :name, :price, :weight def initialize(name) @name = name end def add_delivery_costs @price += (@weight > 100 ? 10 : 5) end end
Utilisation de la méthode tap
sur une instance de Product
:
Product.new('Smartphone X7S').tap do |p| p.price = 99.99 p.weight = 152 p.add_delivery_costs end
Retournera une instance de Product
:
#<Product:0x007fc4f601fb18 @name="Smartphone X7S", @price=109.99, @weight=152>
L'utilisation de yield_self
, retournera le résultat de la dernière méthode utilisée au sein du bloc :
Product.new('Smartphone X7S').yield_self do |p| p.price = 99.99 p.weight = 152 p.add_delivery_costs end
109.99
Le résultat est tout sauf surprenant.
Utilisation de Object#yield_self
Syntaxe avant ruby 2.5 :
CSV.parse(File.read(File.expand_path('data.csv'), __dir__)) .map { |row| row[1].to_i } .sum
À partir de ruby 2.5 :
'data.csv' .yield_self { |name| File.expand_path(name, __dir__) } .yield_self { |path| File.read(path) } .yield_self { |body| CSV.parse(body) } .map { |row| row[1].to_i } .sum
Mieux ? Pire ? Il n'y a pas vraiment de réponse catégorique…
On peut trouver divers bénéfices à cette nouvelle syntaxe :
- une séquence naturelle du haut vers le bas
- un code plus modifiable, toute nouvelle addition n’altérera pas la lisibilité
On peut aussi souligner divers désavantages :
- plus verbeux que l'original
- inhabituel (
idiomatique) en Ruby, évidemment puisqu'il s'agit d'une nouvelle fonctionnalité
Un dernier exemple :
require 'uri' require 'json' require 'net/http' 'https://api.github.com/repos/ruby/ruby' .yield_self { |url| URI.parse(url) } .yield_self { |url| Net::HTTP.get(url) } .yield_self { |response| JSON.parse(response) } .yield_self { |repo| repo.fetch('stargazers_count') } .yield_self { |stargazers| "Ruby has #{stargazers} stargazers" }
Conclusion
Object#yield_self
est une nouvelle méthode qui sera, certainement, très utile en Ruby 2.5.0 pour réaliser un « pipeline » passant les données d'un bloc vers le suivant. On peut regretter que le nom de la méthode ne soit pas plus concis. Cette fonctionnalité arrive alors qu'une certaine ébullition avait secoué la communauté autour de projets tels que, et sans être exhaustif, piped_ruby, elixirize ou pipe_envy. On peut, vraisemblablement, s'attendre à une rapide adoption puisque la demande semble présente.