49 votes

gotchas de method_missing en Ruby

Y a-t-il des choses auxquelles il faut faire attention lors de la définition de la method_missing en Ruby ? Je me demande s'il y a des interactions pas si évidentes que ça entre l'héritage, la levée d'exceptions, les performances ou autre chose.

60voto

James A. Rosen Points 25774

Un point assez évident : toujours redéfinir respond_to? si vous redéfinissez method_missing . Si method_missing(:sym) travaux, respond_to?(:sym) devrait toujours retourner vrai. De nombreuses bibliothèques s'appuient sur ce principe.

Plus tard :

Un exemple :

# Wrap a Foo; don't expose the internal guts.
# Pass any method that starts with 'a' on to the
# Foo.
class FooWrapper
  def initialize(foo)
    @foo = foo
  end
  def some_method_that_doesnt_start_with_a
    'bar'
  end
  def a_method_that_does_start_with_a
    'baz'
  end
  def respond_to?(sym, include_private = false)
    pass_sym_to_foo?(sym) || super(sym, include_private)
  end
  def method_missing(sym, *args, &block)
    return foo.call(sym, *args, &block) if pass_sym_to_foo?(sym)
    super(sym, *args, &block)
  end
  private
  def pass_sym_to_foo?(sym)
    sym.to_s =~ /^a/ && @foo.respond_to?(sym)
  end
end

class Foo
  def argh
    'argh'
  end
  def blech
    'blech'
  end
end

w = FooWrapper.new(Foo.new)

w.respond_to?(:some_method_that_doesnt_start_with_a)
# => true
w.some_method_that_doesnt_start_with_a
# => 'bar'

w.respond_to?(:a_method_that_does_start_with_a)
# => true
w.a_method_that_does_start_with_a
# => 'baz'

w.respond_to?(:argh)
# => true
w.argh
# => 'argh'

w.respond_to?(:blech)
# => false
w.blech
# NoMethodError

w.respond_to?(:glem!)
# => false
w.glem!
# NoMethodError

w.respond_to?(:apples?)
w.apples?
# NoMethodError

13voto

Andrew Grimm Points 22996

Si votre méthode manquante ne recherche que certains noms de méthodes, n'oubliez pas d'appeler super si vous n'avez pas trouvé ce que vous cherchez, afin que les autres méthodes manquantes puissent faire leur travail.

11voto

Pistos Points 8997

Si vous pouvez anticiper les noms des méthodes, il est préférable de les déclarer dynamiquement plutôt que de compter sur method_missing, car method_missing entraîne une pénalité de performance. Par exemple, supposons que vous vouliez étendre un handle de base de données pour pouvoir accéder aux vues de la base de données avec cette syntaxe :

selected_view_rows = @dbh.viewname( :column => value, ... )

Plutôt que de s'appuyer sur method_missing sur le handle de la base de données et d'envoyer le nom de la méthode à la base de données en tant que nom d'une vue, vous pourriez déterminer toutes les vues de la base de données à l'avance, puis itérer sur elles pour créer des méthodes "viewname" sur @dbh.

6voto

James A. Rosen Points 25774

Construire sur Le point de vue de Pistos : method_missing est au moins un ordre de grandeur plus lent que l'appel de méthode ordinaire dans toutes les implémentations Ruby que j'ai essayées. Il a raison d'anticiper quand c'est possible pour éviter les appels à method_missing .

Si vous vous sentez l'âme d'un aventurier, allez voir le site peu connu de Ruby. Délégué classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X