【问题标题】:Ruby: How to eager load class contents before a module gets loaded in its parent classRuby:如何在模块加载到其父类中之前急切加载类内容
【发布时间】: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") 也会失败

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

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

【问题讨论】:

    标签: ruby-on-rails ruby module class-eval ruby-2.6


    【解决方案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
    猜你喜欢
    • 2018-07-10
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    相关资源
    最近更新 更多