Pregunta

Soy todavía nuevo a Ruby y básicamente sólo escribir mi primer micro-programa después de terminar el libro de Cooper. Se me señaló a la dirección de evitar la aplicación de parches mono pero el problema es que no sé cuáles son las alternativas para conseguir el mismo comportamiento. Básicamente, quiero añadir un nuevo método que se puede acceder en cada objeto de cadena. La forma de mono-parches obvia es:

class String
  def do_magic
    ...magic...
  end
end

Recuerdo que hay una manera usando String.send. Pero no puedo recordar cómo se hace ni dónde lo leí. ¿Puede alguien señalar las alternativas que todavía me dejaron hacer que el método disponible para las clases y objetos secundarios de Cuerda?

¿Fue útil?

Solución

Cualquier otra forma de hacer esto sería sólo una sintaxis más incómoda para el mono de parches. Hay maneras que implican send y eval y todo tipo de cosas, pero ¿Por qué ? Seguir adelante y hacerlo de la manera obvia.

¿Quieres ser cuidadoso de parches mono en grandes proyectos o cuando tiene dependencias, porque se puede terminar con los conflictos cuando varias manos están ensuciando alrededor en el mismo lugar. Esto no quiere decir look para una sintaxis alternativa que lleva a cabo la misma cosa - que significa tener cuidado cuando estás haciendo los cambios que podrían afectar código que no es el suyo. Esto probablemente no es una preocupación en su caso particular. Es sólo algo que podría necesitar ser abordado en los proyectos más grandes.

Una de las alternativas en Ruby es que se puede añadir métodos a un solo objeto.

a = "Hello"
b = "Goodbye"
class <<a
  def to_slang
    "yo"
  end
end
a.to_slang # => "yo"
b.to_slang # NoMethodError: undefined method `to_slang' for "Goodbye":String

Otros consejos

Si desea agregar un nuevo método que se puede acceder a cada objeto de cadena, a continuación, haciendo de la manera que lo tienes es cómo lograr que se haga.

Una buena práctica es poner sus extensiones a los objetos básicos en un archivo separado (como string_ex.rb) o un subdirectorio (como extensions o core_ext). De esta manera, es obvio lo que se ha extendido, y es fácil para alguien para ver cómo se han ampliado o modificado.

Cuando el mono parcheo puede ir mal es cuando se cambia un comportamiento existente de un objeto básico que causa algún otro código que se espera que la funcionalidad original a portarse mal.

La clase define object send, y todos los objetos heredan esto. Usted "enviar" un objeto del método send. los parámetros del método send son el nombre del método invoke-te-quiero-a-como un símbolo, seguido de los argumentos y un bloque opcional. También puede utilizar __send__.

>> "heya".send :reverse
=> "ayeh"

>> space = %w( moon star sun galaxy )
>> space.send(:collect) { |el| el.send :upcase! }
=> ["MOON", "STAR", "SUN", "GALAXY"]

Editar ..

Es posible que desee utilizar el método define_method:

String.class_eval {
  define_method :hello do |name|
    self.gsub(/(\w+)/,'hello') + " #{name}"
  end
}

puts "Once upon a time".hello("Dylan")
# >> hello hello hello hello Dylan

Esto añade métodos de instancia. Para añadir métodos de clase:

eigenclass = class << String; self; end
eigenclass.class_eval {
  define_method :hello do
    "hello"
  end
}

puts String.hello
# >> hello

No se puede definir métodos que esperan un bloque sin embargo.

Puede ser que sea una buena cosa para tener una lectura de este capítulo de la Guía de Conmovedor Por qué , que puede pasar a "matriz de Dwemthy" para llegar a la meta materia de programación.

Gracias chicos.

Todo el trabajo de aplicación sugerida. Más importante, he aprendido a sopesar en el caso que nos ocupa y decidir si núcleo reapertura (o biblioteca) clases es una buena idea o no.

Fwiw, un amigo señaló la implementación send que estaba buscando. Pero ahora que lo miro, es incluso más cerca de monkeypatching que todas las otras implementaciones:)

module M
    def do_magic
    ....
    end
end
String.send(:include, M)

Como alternativa a la fijación de las funciones a clases u objetos, siempre se puede ir a la ruta funcional:

class StringMagic
  def self.do(string)
     ...
  end
end

StringMagic.do("I'm a String.") # => "I'm a MAGIC String!"

El "parche mono" que usted describe de hecho podría ser un problema si alguien más quiere requerir su código (como una joya, por ejemplo). ¿Quién puede decir que no se quiera añadir también un método de cadena que se llama do_magic? Un método sobrescribirá la otra, y esto puede ser difícil de depurar. Si hay alguna posibilidad de que su código será de código abierto, entonces lo mejor es crear su propia clase:

class MyString < String
  def initialize(str)
    @str = str
  end
  def do_magic
    ...magic done on @str
    @str
  end
end

Ahora, si necesita do_magic puede

magic_str = MyString.new(str).do_magic
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top