Ruby:将一个方法添加到输入参数的类中

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被定义,因为我把它添加到了类中.
这可能吗?

Jör*_*tag 7

这是你最接近的.我冒昧地将它改为标准的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_methodprivate,所以你需要使用反射来规避访问限制:

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)