Go to Hackademy website

Un peu de magie avec to_proc

Martin Catty

Posté par dans la catégorie back

  • Tags de l'article
  • fr

Vous le savez, ici on aime bien démystifier les comportements pseudo magiques que nous offrent ruby et rails.

D’ailleurs il est parfois difficile de savoir si un bout de code vient du langage ou du framework.

À tel point qu’on a des quiz dédiés pour tenter de deviner quand il s’agit de ruby ou rails.

Alors, ce snippet, et notamment &:, ruby ou rails ?

(1..10).map(&:to_s)

Les deux mon général !

Remontons le temps

Et oui une fois n’est pas coutume, le &: nous vient de Ruby on Rails mais a été ré-intégré directement dans le langage, à partir de la version 1.8.7.

On en oublierait presque que notre chère version 1.8.6 aurait renvoyé un TypeError: wrong argument type Symbol (expected Proc).

Comment ça marche ?

Soyons clair, même en 1.8.6, il était possible d’invoquer un bloc de code avec &, simplement on ne pouvait pas le faire sur un symbole et donc pas par le biais de &:.

Par exemple, ce code fonctionne parfaitement, même en 1.8.6.

class Hello
  def self.to_proc
    proc { puts "Hello" }
  end
end

(1..10).map(&Hello)

À partir du moment ou un objet est «procable» (si vous avez une version FR je prends en commentaire) on peut l’invoquer de la sorte. Et pour le rendre procable, il suffit de définir to_proc et le comportement associé.

L’avantage du to_proc, c’est qu’il transmet automatiquement son contexte :

class Hello
  def self.to_proc
    proc { |i| puts "Hello from #{i}" }
  end
end

(1..10).map(&Hello)

(1..10).map do |index|
  Hello.to_proc.call(index)
end

Les deux dernières instructions font la même chose, mais avouez que la première est plus élégante. Dans tous les cas le contenu de notre itérateur est automatiquement passé à la proc.

Ce qui veut dire qu’en rendant un élément de base procable on pourrait très bien appeler dynamiquement des méthodes sur les éléments de l’itérateur.

Imaginons que nous aimerions pouvoir faire ceci :

(1..10).map(&'to_s')

Il suffit de rendre nos string propres. Euh, rendre nos String proc(able).

class String
  def to_proc
    proc { |context| context.send(self) } # dans notre appel self sera donc 'to_s'
  end
end

Voilà, c’est tout ce dont nous avons besoin. Le choix a été fait de rendre procable les symboles plutôt que les String (et c’est mieux ainsi), mais le fonctionnement est strictement identique.

Même les Hash s’y mettent

Dans la récente release de Ruby 2.3, les Hash aussi deviennent procable. Rien de très excitant, cela nous permettra de récupérer la valeur depuis la clé.

hash = { a: 1, b: 2, c: 3 }
hash.to_proc.call(:a) #=> 1

L’avantage c’est qu’on pourra également l’invoquer depuis un itérateur :

[:a, :b, :z].map(&hash) #=> [1, 2, nil]

L’équipe Synbioz.

Libres d’être ensemble.

Articles connexes

Blocks, Proc et Lambda en Ruby

03/02/2015

Les closures (Block, Proc & Lambda) Ruby sont l’un des aspects les plus puissants du language. Ce sont des fonctions liées à l’environement dans lequel elles ont été définies. Elles ont la particularité de garder l’accés à des variables présente…

Afficher plus Afficher moins

Ajouter un commentaire