Aujourd’hui un aperçu de la méthode super pour les débutants en Ruby. C’est une méthode dont le comportement peut surprendre si vous venez de certains autres langages…

Pour étudier le comportement de super il va nous falloir utiliser l’héritage. Voici une classe de base toute simple:

class Base
  def foo(bar)
    puts "#{bar} from Base"
  end
end

Et voici comment l’utiliser:

base = Base.new
base.foo("Hello")
#=> Hello from Base

Maintenant créons une classe fille qui hérite de Base et redéfinissons la méthode foo:

class Child < Base
  def foo(bar)
    super
    puts "#{bar} from Child"
  end
end

Voici ce que ça donne:

child = Child.new
child.foo("Hello")
#=> Hello from Base
#=> Hello from Child

Il faut noter que:

  1. La méthode éponyme foo de la classe de base n’est pas appelée implicitement. Il faut le faire explicitement avec super.
  2. On est pas limité à un constructeur, on peut appeler super dans une simple méthode.
  3. Dans ce cas précis, pas besoin de passer l’argument bar à la méthode super, c’est fait automagiquement.

Allons plus loin et faisons faire plus de choses à la méthode foo de la classe fille:

class Child < Base
  def foo(bar, baz)
    super
    puts "#{bar} #{baz} from Child"
  end
end

Cette fois-ci la magie n’opère plus et nous avons droit à une belle erreur:

child = Child.new
child.foo("Hello", "world")
#=> super.rb:2:in `foo': wrong number of arguments (2 for 1) (ArgumentError)

Ruby nous signale que la méthode foo de la classe Base a reçu 2 arguments, alors qu’elle n’en attendait qu’un seul ! Pourquoi, alors que nous n’avons même pas passé un seul argument ? Parce que super, sans arguments, prends tous les arguments passés à la méthode dans laquelle il se trouve et les envoient tous vers la méthode éponyme de la classe de base…

Alors comment on s’en sort ? Très simplement en passant à super les paramètres que l’on veut:

class Child < Base
  def foo(bar, baz)
    super(bar)
    puts "#{bar} #{baz} from Child"
  end
end

Et cette fois-ci, ça fonctionne parfaitement:

child = Child.new
child.foo("Hello", "world")
#=> Hello from Base
#=> Hello world from Child

À demain.