Ruby monkey修补核心类的替代方案
我对Ruby还是一个新手,基本上刚刚写完库珀的书,写了我的第一个微程序。我被指向了避免猴子修补的方向,但问题是我不知道实现相同行为的替代方案是什么。 基本上,我想添加一个每个string对象都可以访问的新方法。显而易见的猴子修补方法是:Ruby monkey修补核心类的替代方案,ruby,oop,monkeypatching,Ruby,Oop,Monkeypatching,我对Ruby还是一个新手,基本上刚刚写完库珀的书,写了我的第一个微程序。我被指向了避免猴子修补的方向,但问题是我不知道实现相同行为的替代方案是什么。 基本上,我想添加一个每个string对象都可以访问的新方法。显而易见的猴子修补方法是: class String def do_magic ...magic... end end 我记得有一种使用String.send的方法。但我不记得是怎么做的,也不记得在哪里读的。 有谁能指出一些替代方法,让我仍然可以将该方法用于String类
class String
def do_magic
...magic...
end
end
我记得有一种使用String.send的方法。但我不记得是怎么做的,也不记得在哪里读的。
有谁能指出一些替代方法,让我仍然可以将该方法用于String类和子对象吗?对于monkey patching来说,任何其他方法都是一种更笨拙的语法。有很多方法包括
send
和eval
以及各种各样的事情,但是为什么呢?去吧,用显而易见的方式去做
在大型项目中或当您有依赖关系时,您需要小心猴子补丁,因为当几只手在同一个地方乱搞时,您可能会遇到冲突。这并不意味着寻找一种替代的语法来完成同样的事情——这意味着在进行可能会影响不属于您的代码的更改时要小心。在你的特殊情况下,这可能不是一个问题。这只是一些可能需要在大型项目中解决的问题
Ruby中的另一种选择是,可以向单个对象添加方法
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
a=“你好”
b=“再见”
类对象类定义send
,所有对象都继承该类。您可以使用send
方法“发送”对象。send
方法的参数是您要调用的方法,该方法的名称作为符号,后跟任何参数和可选块。您也可以使用\uuuuu发送\uuuuuu
>> "heya".send :reverse
=> "ayeh"
>> space = %w( moon star sun galaxy )
>> space.send(:collect) { |el| el.send :upcase! }
=> ["MOON", "STAR", "SUN", "GALAXY"]
编辑..
您可能希望使用define\u方法
方法:
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
这增加了实例方法。要添加类方法,请执行以下操作:
eigenclass = class << String; self; end
eigenclass.class_eval {
define_method :hello do
"hello"
end
}
puts String.hello
# >> hello
eigenclass=class>hello
但是,您不能定义需要块的方法
阅读可能是一件好事,你可以跳到“Dwemthy的数组”来了解元编程的内容。如果你想添加一个每个字符串对象都可以访问的新方法,那么按照你现有的方式来做就是如何完成它
一个好的做法是将核心对象的扩展名放在单独的文件(如string\u ex.rb
)或子目录(如extensions
或core\u ext
)中。通过这种方式,很明显是什么被扩展了,人们很容易看到他们是如何被扩展或改变的
monkey patching可能出现问题的地方是,当您更改核心对象的某些现有行为时,会导致一些其他代码期望原始功能出现错误。谢谢大家
所有建议的实施工作。更重要的是,我学会了权衡手头的案例,并决定是否重新打开核心(或库)类是一个好主意
FWIW,一位朋友指出了我正在寻找的send
实现。但现在我看了一下,它比其他所有实现更接近monkeypatching:)
作为将函数附加到类或对象的替代方法,您可以始终按照函数路径:
class StringMagic
def self.do(string)
...
end
end
StringMagic.do("I'm a String.") # => "I'm a MAGIC String!"
如果其他人需要您的代码(例如gem),那么您描述的“猴子补丁”可能确实是个问题。谁说他们不想添加一个名为do_magic的字符串方法?一种方法将覆盖另一种方法,这对调试来说是一个挑战。如果您的代码有可能是开源的,那么最好创建您自己的类:
class MyString < String
def initialize(str)
@str = str
end
def do_magic
...magic done on @str
@str
end
end
我可以看出这实际上取决于项目本身的情况和性质。我认为这也适用于所有的做/不做对。考虑到我的特殊情况,是的,这是有道理的,它不应该伤害。毕竟,我正在添加一种新方法。谢谢你的澄清!猴子修补是一个滑坡,最多。对于封闭源代码项目中的小型团队来说,这可能看起来很优雅,但您永远不知道什么时候您的源代码可能会打包为gem或被其他人复制,如果他们希望他们的字符串以稍微不同的方式发挥作用,那么一个猴子补丁的方法将覆盖另一个,并且不会抛出任何警告。这是一个很难找到的错误食谱。如果要覆盖Ruby中包含的基类的方法,问题就更大了。我当时正试图暂时避免跳入元编程。但我想这是不可避免的,如果我想通过hello world的东西。我关于使用evals的想法基本上是运行时修补程序。但我现在明白了,其他一切都是这样。
class MyString < String
def initialize(str)
@str = str
end
def do_magic
...magic done on @str
@str
end
end
magic_str = MyString.new(str).do_magic