que - funcion ruby
¿Cómo llamar a los métodos de forma dinámica en función de su nombre? (5)
Existen múltiples formas de lograr un despacho dinámico en Ruby, cada una con sus propias ventajas y desventajas. Se debe tener cuidado de seleccionar el método más apropiado para la situación.
La siguiente tabla desglosa algunas de las técnicas más comunes:
+---------------+-----------------+-----------------+------------+------------+
| Method | Arbitrary Code? | Access Private? | Dangerous? | Fastest On |
+---------------+-----------------+-----------------+------------+------------+
| eval | Yes | No | Yes | TBD |
| instance_eval | Yes | No | Yes | TBD |
| send | No | Yes | Yes | TBD |
| public_send | No | No | Yes | TBD |
| method | No | Yes | Yes | TBD |
+---------------+-----------------+-----------------+------------+------------+
Código Arbitrario
Algunas técnicas están limitadas a los métodos de llamadas solamente, mientras que otras pueden ejecutar básicamente cualquier cosa. Los métodos que permiten la ejecución de código arbitrario deben usarse con extrema precaución, si no se evitan por completo .
Acceso privado
Algunas técnicas se limitan a llamar solo a los métodos públicos, mientras que otras pueden llamar a métodos públicos y privados. Idealmente, debe esforzarse por utilizar el método con la menor cantidad de visibilidad que cumpla con sus requisitos.
Nota : Si una técnica puede ejecutar código arbitrario, puede usarse fácilmente para acceder a métodos privados a los que de otro modo no podría tener acceso.
Peligroso
El hecho de que una técnica no pueda ejecutar código arbitrario o llamar a un método privado no significa que sea seguro, especialmente si está utilizando valores proporcionados por el usuario. Eliminar es un método público.
Lo más rápido posible
Algunas de estas técnicas pueden ser más efectivas que otras, según tu versión de Ruby. Puntos de referencia a seguir ....
Ejemplos
class MyClass
def foo(*args); end
private
def bar(*args); end
end
obj = MyClass.new
eval
eval(''obj.foo'') #=> nil
eval(''obj.bar'') #=> NoMethodError: private method `bar'' called
# With arguments:
eval(''obj.foo(:arg1, :arg2)'') #=> nil
eval(''obj.bar(:arg1, :arg2)'') #=> NoMethodError: private method `bar'' called
instance_eval
obj.instance_eval(''foo'') #=> nil
obj.instance_eval(''bar'') #=> nil
# With arguments:
obj.instance_eval(''foo(:arg1, :arg2)'') #=> nil
obj.instance_eval(''bar(:arg1, :arg2)'') #=> nil
enviar
obj.send(''foo'') #=> nil
obj.send(''bar'') #=> nil
# With arguments:
obj.send(''foo'', :arg1, :arg2) #=> nil
obj.send(''bar'', :arg1, :arg2) #=> nil
public_send
obj.public_send(''foo'') #=> nil
obj.public_send(''bar'') #=> NoMethodError: private method `bar'' called
# With arguments:
obj.public_send(''foo'', :arg1, :arg2) #=> nil
obj.public_send(''bar'', :arg1, :arg2) #=> NoMethodError: private method `bar'' called
método
obj.method(''foo'').call #=> nil
obj.method(''bar'').call #=> nil
# With arguments:
obj.method(''foo'').call(:arg1, :arg2) #=> nil
obj.method(''bar'').call(:arg1, :arg2) #=> nil
¿Cómo puedo llamar un método dinámicamente cuando su nombre está contenido en una variable de cadena? Por ejemplo:
class MyClass
def foo; end
def bar; end
end
obj = MyClass.new
str = get_data_from_user # e.g. `gets`, `params`, DB access, etc.
str #=> "foo"
# somehow call `foo` on `obj` using the value in `str`.
¿Cómo puedo hacer esto? ¿Es un riesgo de seguridad?
Lo que quiere hacer se llama despacho dinámico . Es muy fácil en Ruby, solo usa public_send
:
method_name = ''foobar''
obj.public_send(method_name) if obj.respond_to? method_name
Si el método es privado / protegido, use send
lugar, pero prefiera public_send
.
Este es un posible riesgo de seguridad si el valor de method_name
proviene del usuario. Para evitar vulnerabilidades, debe validar qué métodos se pueden llamar realmente. Por ejemplo:
if obj.respond_to?(method_name) && %w[foo bar].include?(method_name)
obj.send(method_name)
end
Puede verificar la disponibilidad del método usando respond_to?
. Si está disponible, entonces llama a send
. Por ejemplo:
if obj.respond_to?(str)
obj.send(str)
end
Use send
para llamar a un método de forma dinámica:
obj.send(str)
Realmente vas a querer tener cuidado con esto. El uso de los datos del usuario para llamar a cualquier método mediante el send
podría dejar espacio para que los usuarios ejecuten cualquier método que deseen. send
se usa a menudo para llamar dinámicamente a los nombres de los métodos, pero asegúrese de que los valores de entrada sean confiables y no puedan ser manipulados por los usuarios.
La regla de oro nunca confía en ninguna entrada que provenga del usuario.