sintax length escape ruby eval

ruby - length - Se supone que ''eval'' es desagradable?



haml sintax (7)

En Ruby hay varios trucos que podrían ser más apropiados que eval() :

  1. Hay #send que le permite llamar a un método cuyo nombre tiene como cadena y pasarle parámetros.
  2. yield permite pasar un bloque de código a un método que se ejecutará en el contexto del método de recepción.
  3. A menudo, el simple Kernel.const_get("String") es suficiente para obtener la clase cuyo nombre tiene como cadena.

Creo que no puedo explicarlos adecuadamente en detalle, así que solo te di los consejos, si estás interesado, vas a googlear.

He estado usando la función eval de ruby ​​muchas veces. Pero he escuchado a personas diciendo que las eval son desagradables. Cuando me preguntan, por qué y cómo, nunca podría obtener una razón convincente para no usarlo. ¿Son realmente desagradables? Si es así, ¿de qué manera? ¿Cuáles son las posibles opciones "más seguras" para evaluar?


En ciertas situaciones, una eval bien ubicada es inteligente y reduce la cantidad de código requerido. Además de las preocupaciones de seguridad que ha mencionado Matt J, también debe hacerse una pregunta muy simple:

Cuando todo está dicho y hecho, ¿alguien más puede leer su código y entender lo que hizo?

Si la respuesta es no, entonces lo que ha ganado con un eval se renuncia a la capacidad de mantenimiento. Este problema no solo es aplicable si trabaja en equipo, sino que también es aplicable a usted; desea poder mirar hacia atrás en los meses de su código, si no en los próximos años, y saber lo que hizo.


Eval es una característica increíblemente poderosa que debe usarse con cuidado. Además de los problemas de seguridad señalados por Matt J, también encontrará que el código evaluado de tiempo de ejecución de depuración es extremadamente difícil. Un problema en un bloque de código evaluado en tiempo de ejecución será difícil de expresar para el intérprete, por lo que será difícil buscarlo.

Dicho esto, si se siente cómodo con ese problema y no está preocupado por el problema de seguridad, entonces no debe evitar el uso de una de las características que hace que Ruby sea tan atractivo como lo es.


Hace que la depuración sea difícil. Hace la optimización difícil. Pero, sobre todo, generalmente es una señal de que hay una mejor manera de hacer lo que sea que intentes hacer.

Si nos dice lo que está tratando de lograr con eval , puede obtener respuestas más relevantes relacionadas con su situación específica.


Si está eval una cadena enviada por, o modificable por el usuario, esto es equivalente a permitir la ejecución de código arbitrario. Imagine si la cadena contiene una llamada a rm -rf / o similar. Dicho esto, en situaciones en las que se sabe que las cadenas están limitadas adecuadamente, o que el intérprete de Ruby se encuentra en una zona de pruebas apropiada, o idealmente ambas, eval puede ser extraordinariamente poderoso.

El problema es análogo a la inyección SQL , si está familiarizado. La solución aquí es similar a la solución al problema de inyección (consultas parametrizadas). Es decir, si se sabe que las declaraciones que desea eval son de una forma muy específica, y no es necesario que el usuario presente todas las declaraciones, solo unas pocas variables, una expresión matemática o similar, puede tomar en cuenta. Estas pequeñas piezas del usuario las desinfectarán si es necesario, luego evaluarán la declaración de la plantilla segura con la entrada del usuario conectada en los lugares apropiados.


Si pasas algo de lo que obtienes del "exterior" para eval , estás haciendo algo mal, y es muy desagradable. Es muy difícil escapar del código lo suficiente como para que sea seguro, por lo que lo consideraría bastante inseguro. Sin embargo, si usa eval para evitar la duplicación u otras cosas similares, como el siguiente ejemplo de código, está bien usarlo.

class Foo def self.define_getters(*symbols) symbols.each do |symbol| eval "def #{symbol}; @#{symbol}; end" end end define_getters :foo, :bar, :baz end

Sin embargo, al menos en Ruby 1.9.1, Ruby tiene métodos de meta-programación realmente potentes, y en su lugar podrías hacer lo siguiente:

class Foo def self.define_getters(*symbols) symbols.each do |symbol| define_method(symbol) { instance_variable_get(symbol) } end end define_getters :foo, :bar, :baz end

Para la mayoría de los propósitos, quiere utilizar estos métodos, y no se necesita escapar.

La otra cosa mala de eval es el hecho de que (al menos en Ruby), es bastante lento, ya que el intérprete necesita analizar la cadena y luego ejecutar el código dentro del enlace actual. Los otros métodos llaman directamente a la función C y, por lo tanto, debe obtener un aumento considerable de la velocidad.


eval no solo es inseguro (como se ha señalado en otra parte), sino que también es lento. Cada vez que se ejecuta, la AST del código evaluado debe analizarse (y, por ejemplo, JRuby, se volvió a bytecode) de nuevo, que es una operación pesada de cadenas y probablemente también sea mala para la localidad de caché (bajo el supuesto de que el programa en ejecución no eval mucho, y las partes correspondientes del intérprete son, por lo tanto, caché-fría, además de ser grandes).

¿Por qué hay eval en Ruby, preguntas? "Porque podemos" en su mayoría - De hecho, cuando se inventó la eval (para el lenguaje de programación LISP), ¡era principalmente para mostrar ! Más eval , usar eval es Lo correcto cuando se quiere "agregar un intérprete a su intérprete", para tareas de metaprogramación como escribir un preprocesador, un depurador o un motor de plantillas. La idea común para tales aplicaciones es dar masajes a un código de Ruby y llamar a eval , y seguro que es mejor que reinventar e implementar un lenguaje de juguete específico de dominio, una trampa también conocida como la Décima Regla de Greenspun . Las advertencias son: tenga cuidado con los costos, por ejemplo, para un motor de plantillas, haga toda su eval en el momento de la puesta en marcha, no en el tiempo de ejecución; y no eval código que no es de confianza a menos que sepa cómo "domarlo", es decir, seleccionar y aplicar un subconjunto seguro del lenguaje de acuerdo con la teoría de la disciplina de la capacidad . Este último es un trabajo realmente difícil (ver, por ejemplo, cómo se hizo para Java , no estoy al tanto de ningún esfuerzo por Ruby por desgracia).