Comment utiliser les procs en Ruby

Dans ce tuto nous allons apprendre à utiliser un proc.

Exemple avec un Builder

Dans une vidéo Youtube

data = [{email: 'toto@gmail.com'}, {email: 'titi@gmail.com'}]

User = Struct.new(:email, keyword_init: true)

class UserBuilder
  def initialize(data)
    @data = data
  end

  def build
    User.new(@data)
  end

  def self.to_proc
    Proc.new {|data| new(data).build }
  end
end

Il existe deux manières de créer des nouveaux utilisateurs qui sont similaires en termes de résultats.

data.map { |user_data| UserBuilder.new(user_data).build }
# [#<struct User email="toto@gmail.com">, #<struct User email="titi@gmail.com">]
data.map(&UserBuilder)
# [#<struct User email="toto@gmail.com">, #<struct User email="titi@gmail.com">]

Dans la seconde façon nous avons utilisé un proc appelé avec un esperluette.

Que fait l’esperluette ?

data.map(&Builders::User)

Tout d’abord il faut savoir qu’un esperluette qui est passé en argument d’un énumérator va appeler par défaut la méthode to_proc sur l’argument. Dans notre cas le Builders::User.

Qu’est ce qu’un proc ?

Ensuite la méthode to_proc va retourner un proc qui est un block de code.

Un proc est une instance de la classe Proc. C’est un objet qui peut être lié à une variable et réutilisé. Il se définit en appelant Proc.new ou proc suivi d’un block. Un proc va pouvoir récupérer des arguments pour les traiter.

Dans notre exemple il récupère le Hash avec les data des users pour ensuite instancier le builder et appeler la méthode build.

Pour aller plus loin sur les procs, il y’a cet excellent article toujours de RubyCademy.

Autre exemple

Un proc peut aussi aider à rendre plus lisible un case (switch statement) comme dans l’exemple ci-dessous.

def is_even
  Proc.new {|x| x % 2 == 0 }
end

def is_odd
  Proc.new {|x| x % 2 == 1 }
end

number = 4

case number
when is_even
  puts "#{number} is even"
when is_odd
  puts "#{number} is odd"
end

Chainer les procs

Une exemple de RubyCademy nous permet une utilation encore plus avancé des procs inspiré de la programmation fonctionnelle.

strip        = proc { |email| email.strip }
downcase     = proc { |email| email.downcase }
remove_alias = proc { |email| email.gsub(/\+.*@/, '@') }

email = "  Test+alias@example.com   "

(strip >> downcase >> remove_alias).call(email)