开发者

Ruby: Add a method to the class of an input parameter

I'm just exploring ruby and was wondering about the theoretical possibility of adding a method to the class of an object. For example, define a method that takes in a parameter and would add a method to the class of that parameter (not just to the parameter object itself). Something like this example:

class SomeClass
end

class AnotherClass
end

alpha = SomeClass.new
beta = AnotherClass.new

def AddHelloMethodTo pa开发者_StackOverflowram

 # 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

(Excuse me if I have syntax errors / typos I'm REALLY new to this!)

Notice how I don't call the AddHelloMethodTo on gamma but I expect Hello to be defined because I added it to the class.

Is this possible?


This is the closest to what you had. I took the liberty of changing it to standard Ruby coding style, but notice that the only real change is the first line of 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

Originally, you had

def obj.class.hello

This will work, but it doesn't do what you think it does. This will add a singleton method to the class object itself, but it appears you assume that it will add an instance method. If you want to add an instance method, you need to use Module#define_method like this:

obj.class.define_method(:hello)

Except that Module#define_method is private, so you need to use reflection to circumvent that access restriction:

obj.class.send(:define_method, :hello)

Note that I also changed the name of the method from add_hello_method_to to add_hello_method_to_class_of, since, well it doesn't add the hello method to its argument, it adds it to its argument's class.

However, if you do monkey patching like this, it is generally considered good practice to use mixins instead, since then, the mixin shows up in the object's inheritance chain, which leaves anybody debugging that code at least a fighting chance to figure out where the heck that mysterious hello method is coming from:

# 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

Now, you can easily debug this code:

gamma.class.ancestors
# => [AnotherClass, HelloExtension, Object, Kernel, BasicObject]

If someone wonders where the hello method is coming from, then it doesn't take much to figure out that a mixin called HelloExtension probably has something to do with it. And following standard Ruby naming conventions they even know to look in a file named hello_extension.rb

You can even do this:

gamma.method(:hello).source_location
# => ['hello_extension.rb', 3]


Your example (once you fix a small syntax error) adds a class method to the class instead of an instance method.

To define an instance method, you could use class_eval and define the method as if you were in the middle of defining the class:

def AddHelloMethodTo param
  param.class.class_eval do
    def Hello
      "Hello"
    end
  end
end

Now all previous and future instances of the object's class will have access to the Hello method.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜