TJB*_*TJB 1 ruby metaprogramming dynamic
我只是在探索ruby,并且想知道将一个方法添加到对象类的理论可能性.例如,定义一个接受参数的方法,并将方法添加到该参数的类中(而不仅仅是参数对象本身).像这样的例子:
class SomeClass
end
class AnotherClass
end
alpha = SomeClass.new
beta = AnotherClass.new
def AddHelloMethodTo param
# This is where I'm trying to
# add a method to the class of the parameter
def param.class.Hello
"Hello"
end
end
AddHelloMethodTo alpha
AddHelloMethodTo beta
gamma = AnotherClass.new
alpha.Hello
beta.Hello
gamma.Hello
Run Code Online (Sandbox Code Playgroud)
(对不起,如果我有语法错误/错别字我真的很新!)
请注意我怎么不调用AddHelloMethodToon gamma但我希望Hello被定义,因为我把它添加到了类中.
这可能吗?
这是你最接近的.我冒昧地将它改为标准的Ruby编码风格,但请注意,唯一真正的改变是第一行add_hello_method_to_class_of:
class SomeClass; end
class AnotherClass; end
alpha = SomeClass.new
beta = AnotherClass.new
def add_hello_method_to_class_of(obj)
obj.class.send(:define_method, :hello) do
'Hello'
end
end
add_hello_method_to_class_of(alpha)
add_hello_method_to_class_of(beta)
gamma = AnotherClass.new
alpha.hello
beta.hello
gamma.hello
Run Code Online (Sandbox Code Playgroud)
原来,你有
def obj.class.hello
Run Code Online (Sandbox Code Playgroud)
这将有效,但它不会做你认为它做的.这会增加单方法的类对象本身,但现在看来,你认为它会增加一个实例方法.如果要添加实例方法,则需要使用Module#define_method如下:
obj.class.define_method(:hello)
Run Code Online (Sandbox Code Playgroud)
除了Module#define_method是private,所以你需要使用反射来规避访问限制:
obj.class.send(:define_method, :hello)
Run Code Online (Sandbox Code Playgroud)
请注意,我还将方法的名称更改为add_hello_method_toto add_hello_method_to_class_of,因为它不会将hello方法添加到其参数中,而是将其添加到其参数的类中.
但是,如果你像这样进行猴子修补,通常认为使用mixins是好习惯,从那以后,mixin出现在对象的继承链中,这使得任何人调试该代码至少是一个战斗的机会来找出哎说神秘的hello方法来自:
# file: hello_extension.rb
module HelloExtension
def hello
'Hello'
end
end
def add_hello_method_to_class_of(obj)
obj.class.send(:include, HelloExtension)
end
# some other file
require 'hello_extension'
class SomeClass; end
class AnotherClass; end
alpha = SomeClass.new
beta = AnotherClass.new
add_hello_method_to_class_of(alpha)
add_hello_method_to_class_of(beta)
gamma = AnotherClass.new
alpha.hello
beta.hello
gamma.hello
Run Code Online (Sandbox Code Playgroud)
现在,您可以轻松调试此代码:
gamma.class.ancestors
# => [AnotherClass, HelloExtension, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)
如果有人想知道hello方法的来源,那么找出一个被称为mixin的HelloExtension东西可能与它有关.遵循标准的Ruby命名约定,他们甚至知道要查找名为的文件hello_extension.rb
你甚至可以这样做:
gamma.method(:hello).source_location
# => ['hello_extension.rb', 3]
Run Code Online (Sandbox Code Playgroud)