我还是Ruby的新手,基本上只是在完成Cooper的书之后编写我的第一个微程序。我被指向避免猴子修补的方向,但问题是我不知道有什么替代方法可以实现相同的行为。 基本上,我想添加一个每个字符串对象都可以访问的新方法。明显的猴子修补方式是:
class String
def do_magic
...magic...
end
end
我记得有一种使用String.send的方法。但我不记得它是如何完成的,也不记得我在哪里阅读它。 任何人都可以指出任何仍然允许我将该方法用于String类和子对象的替代方法吗?
答案 0 :(得分:15)
执行此操作的任何其他方式只是猴子修补的更尴尬的语法。有一些方法涉及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
答案 1 :(得分:6)
如果你想添加一个每个字符串对象都可以访问的新方法,那么按照它的方式进行操作就是如何完成它。
一个好的做法是将核心对象的扩展放在单独的文件(如string_ex.rb
)或子目录(如extensions
或core_ext
)中。这样,很明显扩展了什么,并且很容易让人看到它们是如何被扩展或改变的。
猴子修补可能会变坏的地方是当您更改核心对象的某些现有行为时,会导致某些其他代码期望原始功能行为不当。
答案 2 :(得分:2)
object
类定义send
,所有对象都继承此项。您使用send
方法“发送”对象。 send
方法的参数是您想要调用的方法的名称作为符号,后跟任何参数和可选块。您也可以使用__send__
。
>> "heya".send :reverse
=> "ayeh"
>> space = %w( moon star sun galaxy )
>> space.send(:collect) { |el| el.send :upcase! }
=> ["MOON", "STAR", "SUN", "GALAXY"]
修改.. 强>
您可能想要使用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
添加实例方法。要添加类方法:
eigenclass = class << String; self; end
eigenclass.class_eval {
define_method :hello do
"hello"
end
}
puts String.hello
# >> hello
您无法定义期望阻止的方法。
阅读this chapter from Why's Poignant Guide可能是一件好事,你可以跳到“Dwemthy的数组”来获取元编程的东西。
答案 3 :(得分:1)
谢谢你们。
所有建议的实施工作。更重要的是,我学会了掌握案例,并决定是否重新开放核心(或图书馆)课程是个好主意。
FWIW,一位朋友指出我正在寻找的send
实施。但是现在我看一下它,它比monkeypatching更接近于所有其他实现:)
module M
def do_magic
....
end
end
String.send(:include, M)
答案 4 :(得分:0)
作为将函数附加到类或对象的替代方法,您始终可以使用功能路径:
class StringMagic
def self.do(string)
...
end
end
StringMagic.do("I'm a String.") # => "I'm a MAGIC String!"
答案 5 :(得分:0)
如果其他人想要您的代码(例如,作为宝石),您描述的“猴子补丁”确实可能是一个问题。谁会说他们也不想添加名为do_magic的String方法?一种方法会覆盖另一种方法,这对调试来说可能很有挑战性。如果您的代码有可能是开源的,那么最好创建自己的类:
class MyString < String
def initialize(str)
@str = str
end
def do_magic
...magic done on @str
@str
end
end
现在,如果你需要do_magic,你可以
magic_str = MyString.new(str).do_magic