Как работает instance_eval и почему DHH его ненавидит?

17

Примерно в 19:00 в его презентации RailsConf Дэвид Хайннемайер Ханссон рассказывает о нижние стороны instance_eval :

  

Долгое время я рыдал и бредил   против instance_eval , что является   концепция отказа от использования   параметр (например, do |people| ) и   просто прямо do something , а затем   оценить, что находится в этом блоке внутри   сфера охвата, из которой вы пришли (I   даже не знаю, является ли это связным   Объяснение)

     

Долгое время мне это не нравилось   потому что в некоторых   смысл. Если вы хотите поставить свой собственный   код в том, что вы собираетесь   вызвать что-то, что уже было   там? Вы собираетесь переопределить   что нибудь? Когда вы получаете   вы можете цеплять   все это, и вы можете знать   [ты] не возишься с кем-нибудь   чужой материал

Это звучало интересно, но: а) я не знаю, как работает instance_eval и б) я не понимаю, почему это может быть плохой / увеличить сложность.

Может кто-нибудь объяснить?

    
задан Tom Lehman 18.06.2010 в 18:43
источник

3 ответа

26

То, что instance_eval делает, заключается в том, что он запускает блок в контексте другого экземпляра. Другими словами, он изменяет значение self , что означает, что он изменяет значение методов экземпляра и переменных экземпляра.

Это создает когнитивное отключение: контекст, в котором выполняется блок, не является контекстом, в котором он отображается на экране.

Позвольте мне продемонстрировать это с небольшим изменением примера @Matt Briggs. Допустим, мы не создаем форму, мы создаем электронное письмо:

mail do |f|
  f.subject @subject
  f.name    name
end

В этом случае @subject является переменной экземпляра объекта your , а name - это метод класса your . Вы можете использовать красивую объектно-ориентированную декомпозицию и сохранить свой объект в переменной.

mail do 
  subject @subject
  name    name # Huh?!?
end

В этом случае @subject является переменной экземпляра объекта mail builder ! Это может быть даже существует ! (Или, что еще хуже, может существовать и содержать какое-то совершенно глупое значение.) no way для доступа к экземпляру объекта your переменные. И как вы даже вызывают метод name вашего объекта? Каждый раз, когда вы пытаетесь вызвать его, вы получаете метод .

В основном, instance_eval затрудняет использование собственного кода внутри DSL-кода. Поэтому он действительно должен использоваться только в тех случаях, когда очень мало шансов, что это может понадобиться.

    
ответ дан Jörg W Mittag 18.06.2010 в 19:51
источник
16

Итак, идея здесь вместо чего-то вроде этого

form_for @obj do |f|
  f.text_field :field
end

вы получите что-то вроде этого

form_for @obj do 
  text_field :field
end

первый способ довольно прямолинейный, вы получаете шаблон, похожий на этот

def form_for
  b = FormBuilder.new
  yield b
  b.fields.each |f|
    # do stuff
  end
end

вы получаете объект-строитель, который потребитель вызывает методы, а затем вы вызываете методы в объекте-компоновщике, чтобы фактически создать форму (или что-то еще)

второй - немного более магический

def form_for &block
  b = FormBuilder.new
  b.instance_eval &block
  b.fields.each |f|
    #do stuff
  end
end

в этом, вместо того, чтобы давать строителю блок, мы берем блок и оцениваем его в контексте компоновщика

Во-вторых, вы увеличиваете сложность, потому что вы являетесь игровыми играми с областью действия, вам нужно понять это, и потребитель должен это понимать, и тот, кто написал ваш строитель, должен это понимать. Если все находятся на одной странице, я не знаю, что это определенно плохо, но я сомневаюсь в преимуществах по сравнению с расходами, я имею в виду, как трудно просто зацепиться за f. перед вашими методами?

    
ответ дан Matt Briggs 18.06.2010 в 18:55
источник
0

Идея состоит в том, что это немного опасно в том, что вы никогда не можете быть абсолютно уверены, что не собираетесь что-то сломать, не читая весь код, связанный с объектом, использующим instance_eval.

Также, если вы, скажем, обновили библиотеку, которая не сильно изменила интерфейс, но изменила многие внутренние объекты объекта, вы действительно можете нанести какой-то ущерб.

    
ответ дан thomasfedb 19.06.2010 в 10:20
источник