Я читаю документацию Module
, но, похоже, не понимаю их отличий и который следует использовать там.
Как eval
отличается от exec
?
Я читаю документацию Module
, но, похоже, не понимаю их отличий и который следует использовать там.
Как eval
отличается от exec
?
Я отвечу немного больше, чем ваш вопрос, включив 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
)
Чтобы ответить на ваш последний вопрос, 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
, но это личное предпочтение.