¿Hay algo de lo que tenga cuidado al definir el método method_missing
en Ruby? Me pregunto si hay algunas interacciones no tan obvias de herencia, lanzamiento de excepción, rendimiento o cualquier otra cosa.method_missing gotchas en Ruby
Respuesta
Algo obvio: siempre redefina respond_to?
si redefine method_missing
. Si method_missing(:sym)
funciona, respond_to?(:sym)
siempre debe devolver verdadero. Hay muchas bibliotecas que confían en esto.
después:
Un ejemplo:
# 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
Si puede anticipar los nombres de método, es mejor declarar dinámicamente que confiar en method_missing porque method_missing incurre en una penalización de rendimiento. Por ejemplo, suponga que desea extender un mango de base de datos para poder acceder a las vistas de base de datos con la siguiente sintaxis:
selected_view_rows = @dbh.viewname(:column => value, ...)
lugar de confiar en method_missing en el mango de base de datos y el envío del nombre del método a la base de datos como el nombre de una vista, puede determinar todas las vistas en la base de datos con anticipación, luego iterar sobre ellas para crear métodos "viewname" en @dbh.
Construyendo en Pistos's point: method_missing
es al menos un orden de magnitud más lento que el método habitual de invocación de todas las implementaciones de Ruby que he probado. Tiene razón al anticipar cuando sea posible para evitar llamadas al method_missing
.
Si te sientes aventurero, echa un vistazo a la poco conocida clase Delegator de Ruby.
Si el método que falta de método solo busca determinados nombres de método, no olvide llamar a super si no ha encontrado lo que está buscando, para que otras pérdidas de método puedan hacer su trabajo.
Sí, de lo contrario, la llamada a su método fallará en silencio y pasará horas tratando de descubrir por qué su método no funciona aunque no haya errores. (No es que hubiera hecho tal cosa) – PhillipKregg
Otra Gotcha:
method_missing
comporta de manera diferente entre obj.call_method
y obj.send(:call_method)
. Esencialmente, el primero omite todos los métodos privados y no definidos, mientras que uno más tarde no se pierde los métodos privados.
Así que method_missing
nunca atrapará la llamada cuando alguien llame a su método privado a través del send
.
respuesta de James es grande, pero, en rubí moderna (1.9+), al igual que Marc-André está diciendo, desea volver a definir respond_to_missing?
, ya que le da acceso a otros métodos en la parte superior de respond_to?
, como method(:method_name)
devolver el método mismo .
ejemplo, la clase siguiente definido:
class UserWrapper
def initialize
@json_user = { first_name: 'Jean', last_name: 'Dupont' }
end
def method_missing(sym, *args, &block)
return @json_user[sym] if @json_user.keys.include?(sym)
super
end
def respond_to_missing?(sym, include_private = false)
@json_user.keys.include?(sym) || super
end
end
Resultados en:
irb(main):015:0> u = UserWrapper.new
=> #<UserWrapper:0x00007fac7b0d3c28 @json_user={:first_name=>"Jean", :last_name=>"Dupont"}>
irb(main):016:0> u.first_name
=> "Jean"
irb(main):017:0> u.respond_to?(:first_name)
=> true
irb(main):018:0> u.method(:first_name)
=> #<Method: UserWrapper#first_name>
irb(main):019:0> u.foo
NoMethodError (undefined method `foo' for #<UserWrapper:0x00007fac7b0d3c28>)
lo tanto, siempre definir respond_to_missing?
cuando anulando method_missing
.
- 1. equivalente en Python de 'method_missing' de Ruby
- 2. Equivalente a Ruby method_missing en Objective C/iOS
- 3. ¿Tiene Javascript algo como la función method_missing de Ruby?
- 4. ¿Ruby tiene un method_missing equivalente para variables de instancia indefinidas?
- 5. ReSharper Gotchas
- 6. .NET Resource Leak Gotchas
- 7. ASP.NET - Común Gotchas
- 8. ASP/VBScript "Gotchas"
- 9. Gotchas para escribir rubygems
- 10. Javascript/jQuery "Gotchas"
- 11. Cómo averiguar lo que está interceptando '' method_missing
- 12. Carga dinámica de clases: ¿hay un "method_missing" para las clases en Ruby?
- 13. Vala: Gotchas, consejos y trucos
- 14. cómo componer módulos que contienen method_missing en rubí
- 15. ruby on rails campos de atributos dinámicos de DB utilizando los problemas de method_missing
- 16. ¿Qué git gotchas has sido atrapado?
- 17. Gotchas/errores en desarrollo para WebKit en iOS o Android
- 18. Gotchas de pasar de desarrollar ASP.NET a aplicaciones de Winforms
- 19. Nivel de pila demasiado profundo en Ruby
- 20. Falta el método Java (ala Ruby) para decorar?
- 21. Jerarquía de llamadas al método Ruby
- 22. Escribiendo en archivos Excel en ruby usando roo gem
- 23. Gotchas al hacer uso de Nullable <T> en C# 4
- 24. gotchas de efecto secundario en python/numpy? historias de terror y escapes estrechos quería
- 25. 'Gotchas' del ferrocarril y curva de aprendizaje: ¿alguna vez se detendrá?
- 26. Ruby: ¿por qué puts llama a_ary?
- 27. Ruby: capturando todos los métodos enviados a un objeto
- 28. Fooling Ruby, ===, con objetos proxy
- 29. ¿Este abuso de la metaprogramación de ruby?
- 30. A su vez en plena traza en Ruby on Rails TestCase
Eso es interesante.¿Cómo implementaría eso para una clase que consta de métodos "normales" y métodos "dinámicos" (implementados a través de method_missing)? –
@Christoph: su método 'pass_sym_to_foo?' Se convierte en un método genérico 'handle?' Que decide si trata de procesar esta solicitud o la transfiere a 'method_missing' de' super'. –
En Ruby 1.9.2, es aún mejor redefinir 'responder_a_missing?', Ver la publicación de mi blog: http://blog.marc-andre.ca/2010/11/methodmissing-politely.html –