Alternativas a las clases básicas de parches mono
-
22-08-2019 - |
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?
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