【问题标题】:Why Rails uses RUBY_EVAL to define methods?为什么 Rails 使用 RUBY_EVAL 来定义方法?
【发布时间】:2013-04-19 16:08:48
【问题描述】:

我正在阅读《制作 Rails 应用程序》这本书,并接触到了 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_#{name} 方法也是动态定义的

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

在我看来,还有 eval 的替代方案。为什么不将那些与键关联的 &blocks 保留在哈希下?那么当调用 _handle_render_options 时,我们会选择正确的块并评估它吗?为什么要构建一个包含大量 if 的方法体是低效的,对吧?

还没收到。

【问题讨论】:

    标签: ruby ruby-on-rails-3 oop dynamic


    【解决方案1】:

    为什么要构建一个包含大量 if 的方法体是低效的,对吧?

    嗯,这不是低效的。如果您在运行时有一个选项哈希,如您所描述的,您必须在运行时循环哈希检查选项列表中的每个可用渲染器。当前的实现基本上将这个循环展开成一堆子句。因此,它实际上会比在运行时循环更快。

    实际上,代码看起来像现在这样的全部原因是性能。这就是为什么我们不保留这些块,因为将它们编译为方法实际上更快。然而,Ruby VM/GC 在每个版本中都会发生变化,这可能不再是真的,或者方法之间的差异可能不再重要,但在最初编写时,它确实如此。

    【讨论】:

    • 为什么不到处使用define_method
    • define_method 稍微贵一些,因为它保留了有调用时需要激活的绑定。我不确定这在最新的 Ruby 版本中是否仍然相关,可能不是。
    猜你喜欢
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-14
    相关资源
    最近更新 更多