问题
class String
def hello
"world"
end
end
String.class_eval {
def world
"hello"
end
}
"a".world
=> "hello"
"b".hello
=> "world"
They seems to do the same thing -- adding a method to a existing class. So what's the difference?
回答1:
With class_eval
you can do more dynamic things:
>> met = "hello" #=> "hello"
>> String.class_eval "def #{met} ; 'hello' ; end" #=> nil
>> "foo".hello #=> "hello"
回答2:
class_eval do conceptually class reopening (or monkey patching). There are mostly syntactic differences. If you pass string to class_eval
(as in Michael's example) you have mostly the same syntax inside the string as in class String; ... end
. If you pass block: String.class_eval { ... }
it compares as follows:
- inside class_eval block outer local variables are visible
- inside reopened class outer local variables are NOT visible
- inside class_eval you CANNOT assign constants and class variables scoped to the class
- inside reopened class you CAN
It would be interesting to know the other differences
回答3:
Other answers are good. Want to add that class_eval
can be used when you want reference class not by its constant or to patch particular object.
e.g.
huh = String
class huh
end
SyntaxError: (eval):2: class/module name must be CONSTANT
huh.class_eval <<-eof
def mamma
puts :papa
end
eof
"asdff".mamma
=> papa
You can use class_eval
to patch particular object without affectin whole root class.
obj = "asd"
obj.singleton_class.class_eval <<-eof
def asd
puts "gah"
end
undef_method :some_method
The above is same as:
class << obj
...
end
instance_eval
will have slightly different behavior by some usage.
I find this question and answers interesting: How to monkey patch a ruby class inside a method
Also there were questions about instance_eval
vs class_eval
but I don't have a link handy.
来源:https://stackoverflow.com/questions/9399358/monkey-patching-vs-class-eval