2013-04-19 3 views
2

Я читал книгу «Ремесла Rails Applications» и получил мои руки на исходный код Rails. Там я нашел несколько примеров RUBY_EVAL, которые заставляли меня думать: почему они это сделали?Почему Rails использует RUBY_EVAL для определения методов?

action_controller/metal/renderers.rb использует его для создания тела для _write_render_options и определения методов _handle_render_options.

def _write_render_options 
    renderers = _renderers.map do |name, value| 
    <<-RUBY_EVAL 
     if options.key?(:#{name}) 
     _process_options(options) 
     return _render_option_#{name}(options.delete(:#{name}), options) 
     end 
    RUBY_EVAL 
    end 

    class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 
    def _handle_render_options(options) 
     #{renderers.join} 
    end 
    RUBY_EVAL 
end 

_render_option _ # {имя} методы определяются на лету, а

RENDERERS = {} 
def self.add(key, &block) 
    define_method("_render_option_#{key}", &block) 
    RENDERERS[key] = block 
    All._write_render_options 
end 

Мне кажется, что есть альтернативы Eval. Почему бы не просто сохранить эти блоки &, связанные с ключами под хэш? Поэтому при вызове _handle_render_options мы бы выбрали правильный блок и оценили его? Зачем строить тело метода с большим количеством if, которое неэффективно, не так ли?

У меня его нет.

ответ

2

Зачем строить корпус метода с большим количеством if, который неэффективен, не так ли?

Ну, это неэффективно. Если у вас есть хэш опций во время выполнения, как вы описали, вам нужно будет зациклировать хэш во время выполнения, проверяя каждый доступный рендерер в списке параметров. Текущая реализация в основном разворачивает этот цикл в кучу статей. Поэтому на самом деле это будет быстрее, чем цикл во время выполнения.

Собственно, вся причина, по которой код выглядит так, как сейчас, является производительностью. Вот почему мы не блокируем блоки, поскольку их компиляция на методы выполняется быстрее. Тем не менее, Ruby VM/GC изменяет каждый выпуск, и это может быть уже не так, или разница между подходами может не иметь значения, но когда это было изначально написано, это произошло.

+1

Почему бы не использовать 'define_method' везде? – user2398029

+0

'define_method' немного дороже, потому что он сохраняет привязку, которая должна быть активирована при вызове. Я не уверен, хотя, если это все еще актуально в последних версиях Ruby, скорее всего, нет. –