Подтвердить что ты не робот

В чем разница между class_eval, class_exec, module_eval и module_exec?

Я читаю документацию Module, но, похоже, не понимаю их отличий и который следует использовать там.

Как eval отличается от exec?

4b9b3361

Ответ 1

Я отвечу немного больше, чем ваш вопрос, включив instance_{eval|exec} в ваш вопрос.

Все изменения {instance|module|class}_{eval|exec} изменяют текущий контекст, т.е. значение для self:

class Array
  p self                     # prints "Array"
  43.instance_eval{ p self } # prints "43"
end

Теперь о различиях. Версии eval принимают строку или блок, а версии exec принимают только блок, но позволяют передавать ему параметры:

def example(&block)
  42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"

Версия eval не позволяет передавать параметры. Он предоставляет self в качестве первого параметра, хотя я не могу придумать для этого использование.

Наконец, module_{eval|exec} совпадает с соответствующим class_{eval|exec}, но они немного отличаются от instance_{eval|exec}, поскольку они изменяют то, что является текущим открытым классом (то есть, что будет затронуто def) по-разному

String.instance_eval{ def foo; end }
Integer.class_eval  { def bar; end }

String.method_defined?(:foo)            # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar)           # => true

Итак, obj.instance_{eval|exec} открывает одноэлементный класс obj, а mod.{class|module}_{eval|exec} открывает mod сам.

Конечно, instance_{eval|exec} доступны на любом объекте Ruby (включая модули), а {class|module}_* доступны только на Module (и, следовательно, Classes)

Ответ 2

Чтобы ответить на ваш последний вопрос, eval (во всех его вариантах) полностью отличается от exec. exec $command запустит новый процесс для запуска указанной вами команды, а затем выйдет, когда это закончится.

class_eval и module_eval имеют право переопределять классы и модули - даже те, которые вы сами не писали. Например, вы можете использовать класс eval для добавления нового метода, которого не было.

Fixnum.class_eval { def number; self; end }
7.number # returns '7'

class_eval можно использовать для добавления методов экземпляра, а instance_eval можно использовать для добавления методов класса (да, эта часть очень запутанна). Метод класса будет похож на Thing.foo - вы буквально вызываете метод foo в классе Thing. Метод экземпляра подобен приведенному выше примеру, используя class_eval. Я добавил метод number для каждого экземпляра Fixnum.

Хорошо, так что класс методов *_eval. Методы exec аналогичны, но они позволяют вам заглянуть внутрь класса и выполнить блок кода, как если бы он был определен как метод для этого класса. Возможно, у вас есть класс, который выглядит следующим образом:

class Foo
  @@secret = 'secret key'
  @@protected = 'some secret value'
  def protected(key)
    if key == @@secret
       return @@protected
    end
  end
end

Класс foo - это просто оболочка вокруг некоторого секретного значения, если вы знаете правильный ключ. Тем не менее, вы можете обмануть класс, чтобы дать вам свои секреты, выполнив блок внутри контекста такого класса:

Foo.class_exec { @@secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of @@protected because we overwrote @@secret

В общем, с большим количеством инструментов в рубине вы можете использовать любой из них, чтобы решить множество проблем. Много времени вам, вероятно, даже не понадобится, если вы не хотите, чтобы обезьяна патчала класс, который определенная вами библиотека определила (хотя это открывает целую банку червей). Попробуйте поиграть с ними в irb и посмотрите, что вам будет легче. Я лично не использую методы *_exec столько, сколько методы *_eval, но это личное предпочтение.