2

我在 Rails 4.2 中有一个可订购的关注点,它有一个恒定的“完成”。app/models/concerns/orderable.rb

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

在 Rails 控制台中,我可以运行Orderable.constants,它返回[:COMPLETE]。但是,如果我将 Orderable 关注点更改为Rails有关模块中描述的“low-cruft”样式,如下所示:

concern :Orderable do
  COMPLETE = "Complete"
end

然后Orderable.constants在 Rails 控制台中运行返回[]. Rails 文档说“定义关注点的简单快捷方式……是等价的”。为什么这个单一的改变会导致无法访问模块的常量?我需要以某种方式重新定义它们吗?

4

1 回答 1

1

它似乎实际上是如何实现关注“宏”的缺陷:

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​​ Module 对象进行猴子补丁以提供一种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

在解决此问题之前,我会避免使用“低俗”的语法。

于 2018-05-04T11:03:31.050 回答