为什么 Rails low-cruft 问题会导致模块丢失其常量?

Why does the Rails low-cruft concern cause the module to lose its constants?

我在 Rails 4.2 中有一个常量 "Complete" 的可订购问题。 app/models/concerns/orderable.rb:

module Orderable
  extend ActiveSupport::Concern
  COMPLETE = "Complete"
end

在 Rails 控制台中,我可以 运行 Orderable.constants,returns [:COMPLETE]。但是,如果我将可订购关注点更改为 Rails concerning module 中描述的 "low-cruft" 样式,如下所示:

concern :Orderable do
  COMPLETE = "Complete"
end

然后 运行在 Rails 控制台 returns [] 中宁 Orderable.constants。 Rails 文档说 "low-cruft short cut to define a concern ... is equivalent." 为什么这个单一的更改会导致失去对模块常量的访问权限?我需要以某种方式重新定义它们吗?

这似乎实际上是关注点 "macro" 的实施方式存在缺陷:

require 'active_support/concern'

class Module
    # A low-cruft shortcut to define a concern.
    #
    #   concern :EventTracking do
    #     ...
    #   end
    #
    # is equivalent to
    #
    #   module EventTracking
    #     extend ActiveSupport::Concern
    #
    #     ...
    #   end
    def concern(topic, &module_definition)
      const_set topic, Module.new {
        extend ::ActiveSupport::Concern
        module_eval(&module_definition)
      }
    end
  end
  include Concerning
end

此代码猴子修补 ruby 模块对象以提供 concern 方法。

这里的关键是module_eval(&module_definition),它没有在定义的新模块的上下文中正确评估块。

当你 运行:

时实际发生了什么
concern :Orderable do
  COMPLETE = "Complete"
end

::COMPLETE
# => "Complete"

您是否在主对象中声明常量 COMPLETE。糟糕!

要正常工作,它应该如下所示:

def concern(topic, &module_definition)
  const_set topic, Module.new do |m|
    extend ::ActiveSupport::Concern
    m.module_eval(&module_definition)
  end
end

在修复此问题之前,我会避免使用 "low-cruft" 语法。