Ruby:如何在模块加载到其父类中之前急切加载类内容

Posted

技术标签:

【中文标题】Ruby:如何在模块加载到其父类中之前急切加载类内容【英文标题】:Ruby: How to eager load class contents before a module gets loaded in its parent class 【发布时间】:2020-04-01 12:11:24 【问题描述】:

我有几个类的常量SCHEMA

class Consumable::ProbeDesign < Consumable
  SCHEMA = 
    "type": "object",
    "properties":   ,
    "required": []
  
end

class DataModule::WaterDeprivationLog < DataModule
  SCHEMA = 
    "type": "object",
    "properties": 
      "water_amount":         "type": "decimal",
      "notes":                "type": "string"
    ,
    "required": []
  
end

它们是 STI 方案中基类的子类


class Consumable < ApplicationRecord
  include SingleTableInheritable
end

class DataModule < ApplicationRecord
  include SingleTableInheritable
end

然后我有一个模块,它需要为从包含该模块的类继承的所有类动态访问该常量

module SingleTableInheritable
  extend ActiveSupport::Concern

  included do
    def self.inherited(subclass)
      subclass.class_eval do
        schema = subclass::SCHEMA # NameError: uninitialized constant #<Class:0x0000560848920be8>::SCHEMA
        # then do some validations that rely on that schema value
      end

      super
    end
  end
end

但在执行时以及在如何调用它的上下文中,它找不到模块并返回NameError: uninitialized constant #&lt;Class:0x0000560848920be8&gt;::SCHEMA

请注意,subclass.const_get("SCHEMA") 也会失败

编辑:这是一个加载顺序问题。在类上运行之后,该常量可用,因为随后加载了该类。但是通过尝试预先加载这个类,模块会在预先加载时从父类继承,并且模块代码在设置常量之前仍然运行。

是否有某种类似于继承但允许所有内容预加载的钩子?

【问题讨论】:

【参考方案1】:

这里的问题实际上是Module#included 将始终在评估子类的主体之前运行。但Module#included 并不是添加验证或回调的唯一方法。

您可以定义自己的“钩子”:

module SingleTableInheritance
  extend ActiveSupport::Concern
  class_methods do
    def define_schema(hash)
      class_eval do
        const_set(:SCHEMA, hash)
        if self::SCHEMA["type"] == "object" 
          validates :something ...
        end
      end
    end
  end
end

define_schema 只是一个普通的旧类方法,它打开了 eigenclass。这与在 Rails 和 Ruby 中普遍使用的模式相同,从生成 setter 和 getter 到验证甚至回调。

用法:

class DataModule::WaterDeprivationLog < DataModule
  define_schema(
    type: "object",
    properties: 
      water_amount:          type: "decimal",
      notes:                 type: "string"
    ,
    required: []
  )
end

您还应该知道,您使用的“短”哈希语法会将键强制转换为符号:

irb(main):033:0> "foo": "bar" 
=> :foo=>"bar"

如果您想将字符串作为键,请改用 hashrockets =&gt;"foo": "bar" 被认为是不好的形式,因为意图很不明确。

【讨论】:

这看起来不错。让我运行它以确保。奇怪的散列语法来自这样一个事实,即这些最初是 json 文件,我试图将其作为散列直接放在类中。 很棒的答案,它不仅解决了我的问题,还让我了解了约定。谢谢@max

以上是关于Ruby:如何在模块加载到其父类中之前急切加载类内容的主要内容,如果未能解决你的问题,请参考以下文章

Angular 8 急切加载模块(包括应用程序模块)不会出现在 chrome 开发工具中

Java 中的急切加载是啥?

如何急切加载具有条件和当前用户的关联表?

急切的加载未加载到导轨中

如何像在 Prime 类中一样在 Ruby 中创建默认实例?

如何在 laravel 中检索用户时急切加载?