Патч обезьяны vs class_eval?

class String
  def hello
    "world"
  end
end

String.class_eval {
  def world
    "hello"
  end
}

"a".world
=> "hello"
"b".hello
=> "world"

Кажется, что они делают то же самое - добавление метода к существующему классу. Итак, какая разница?

Ответ 1

С помощью class_eval вы можете делать более динамичные вещи:

>> met = "hello" #=> "hello"
>> String.class_eval "def #{met} ; 'hello' ; end" #=> nil
>> "foo".hello #=> "hello"

Ответ 2

class_eval делать концептуально повторное открытие класса (или переключение обезьян). В основном существуют синтаксические различия. Если вы передадите строку в class_eval (как в примере Майкла), вы в основном используете тот же синтаксис внутри строки, что и в class String; ... end. Если вы передаете блок: String.class_eval { ... }, он сравнивается следующим образом:

  • внутри класса class_eval блокируются внешние локальные переменные
  • Внутри повторно открытого класса внешние локальные переменные НЕ видны
  • внутри class_eval вы НЕ МОЖЕТЕ назначать константы и переменные класса, привязанные к классу
  • внутри повторно открыт класс вы МОЖЕТ

Было бы интересно узнать другие различия

Ответ 3

Другие ответы хороши. Хотите добавить, что class_eval можно использовать, если вы хотите, чтобы ссылочный класс не был его константой или не исправлял какой-либо конкретный объект.

например.

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

Вы можете использовать class_eval для исправления определенного объекта без влияния на весь корневой класс.

obj = "asd"
obj.singleton_class.class_eval <<-eof
def asd
puts "gah"
end
undef_method :some_method

Вышеуказанное значение равно:

class << obj
  ...
end

instance_eval будет иметь несколько другое поведение при некотором использовании.

Я нахожу этот вопрос и ответы интересным: Как обезглавить патч рубинового класса внутри метода

Также были вопросы о instance_eval vs class_eval, но у меня нет ссылки.