Xavier Nayrac

Rubyiste accro au TDD, serial blogger, apprenti data scientist, heureux utilisateur de Vim, accordéoniste.
Si vous vous sentez particulièrement généreux, suivez moi sur Twitter.

La méta programmation en Ruby - partie 3

| Comments

Niveau : intermédiaire

Aujourd’hui une explication de method_missing, utilisée hier pour écrire le constructeur de requête.

Tout d’abord un peu de pratique:

1
2
3
4
5
6
7
8
9
class Foo
  def method_missing(met)
    puts 'Inside method_missing ---'
    puts met
  end
end

Foo.new.foo
Foo.new.foobarbaz
$ ruby meta3.rb 
Inside method_missing ---
foo
Inside method_missing ---
foobarbaz

Et maintenant la théorie. Lorsque vous passez un message a un objet, comme Foo.new.foo et que ce message (cette méthode) n’existe pas, Ruby regarde si l’objet possède la méthode method_missing et dans ce cas, l’appelle. L’argument passé à method_missing est le nom de la méthode manquante.

Maintenant on ajoute un argument à method_missing, c’est l’argument de la méthode manquante:

1
2
3
4
5
6
7
8
9
class Foo
  def method_missing(met, arg)
    puts 'Inside method_missing ---'
    puts met
    puts arg
  end
end

Foo.new.foo('bar')
$ ruby meta3.rb 
Inside method_missing ---
foo
bar

Alors que ce passe-t-il si on passe plusieurs arguments ? Essayons:

1
Foo.new.foo('bar', 'baz')
meta3.rb:13:in `method_missing': wrong number of arguments (3 for 2) 

Et oui, ça ne fonctionne pas. Comme on ne peut pas connaître à l’avance le nombre d’arguments de la méthode manquante, il est bon de tous les récupérer dans un tableau:

1
2
3
4
5
6
7
8
9
10
11
class Foo
  def method_missing(met, *arg)
    puts 'Inside method_missing ---'
    puts met
    puts arg.inspect
  end
end

Foo.new.foo
Foo.new.foo('bar')
Foo.new.foo('bar', 'baz')

Et dans ce cas là, il n’y a plus de problèmes, on peut gérer n’importe quel nombre d’arguments:

$ ruby meta3.rb 
Inside method_missing ---
foo
[]
Inside method_missing ---
foo
["bar"]
Inside method_missing ---
foo
["bar", "baz"]

Pour finir, il faut noter qu’on peut comme toujours passer un bloc:

1
2
3
4
5
6
7
8
9
10
11
12
class Foo
  def method_missing(met, *arg, &block)
    puts 'Inside method_missing ---'
    puts met
    puts arg.inspect
    puts block.call if block_given?
  end
end

Foo.new.foo('bar') do
  'return from a block'
end
$ ruby meta3.rb 
Inside method_missing ---
foo
["bar"]
return from a block

À demain.

Articles connexes

Commentaires