В чем разница между class_eval, class_exec, module_eval и module_exec? - PullRequest
43 голосов
/ 30 января 2012

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

Чем eval отличается от exec?

Ответы [ 2 ]

84 голосов
/ 30 января 2012

Я собираюсь ответить немного больше, чем на ваш вопрос, включив 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)

6 голосов
/ 30 января 2012

Чтобы ответить на ваш последний вопрос первым, 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...