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 theAddHelloMethodTo
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.
精彩评论