Ruby 在 ActiveSupport::Concern 中使用 class 变量的替代方法

Ruby Alternative to use of class variables in ActiveSupport::Concern

我有一个应用程序需要对数据库中的某些字段使用加密。目前这是使用一个关注点来实现的,该关注点处理包含的 classes 上的加密和管理字段的精细细节。重要的是系统能够以编程方式确定哪些 classes 包含此问题,但更具体地说,需要以编程方式确定哪些字段已加密。

今天使用 class 变量实现了,工作正常:

module EncryptedFields

    extend ActiveSupport::Concern

    included do
        @@encrypted_attributes ||= {}
        @@encrypted_attributes[self.to_s] ||= []

        def self.encrypted attribute, options={}

            @@encrypted_attributes[self.to_s] << attribute

            ### Other stuff
        end
    end
end

包含在一个class中,像这样:

class SomeEcryptedModel

   include EncryptedFields

   encrypted :field_name, options
   encrypted :other_field_name, options

   #etc
end

class 变量 @@encrypted_attributes 将正确捕获键值对的散列,其中包含 class 名称作为键,加密属性数组作为值。我考虑过使用加密模型的注册系统来使用 'register' 自身及其属性,但是与此相关的开销更多,在我的时间表上我想从更简单的事情开始,如果不是的话太不安全了。

这在我当前的应用程序中实际上工作正常,但我没有很多关注点或 class 变量的经验,所以我担心我是否对如何做出严重的错误估计这将表现。这里的陷阱在哪里?自从我开始使用 ruby(不久前)以来,我就被编程为通常要避免使用 class 变量。

我已经被它咬过一次了,因为最初我认为 class 变量 @@encrypted_attributes 将是包含的 class 上的一个 class 变量;事实显然并非如此。每个包含它的新模型都会覆盖它,所以我得出的结论是这个 class 变量显然是关注点本身。至少,这是我似乎正在目睹的行为。最后证明这是一种更理想的行为,因为现在我可以获得加密模型的完整列表。这有一个明显的限制,即它只能 return 已加载模型的加密模型和属性列表。

那么,我的问题是:

这是 class 变量的正确用例,还是有其他(更好的?)方法来捕获相同的信息?如果这是一个可接受的 class 变量用例,我应该添加哪些陷阱 and/or 保护以确保代码按预期工作?

也许我只是太聪明了,我应该硬编码我的列表?感谢您的帮助!

你也可以走这条路关注途径:

module EncryptedFields
  @encrypted_attributes ||= {}

  def self.included(klass)
    @encrypted_attributes[klass.name] ||= []
    klass.extend(ClassMethods)
  end

  def self.add(class_name, attribute)
    @encrypted_attributes[class_name] << attribute
  end

  module ClassMethods
    def encrypted(attribute, options={})
      EncryptedFields.add(name, attribute)
      # Other stuff
    end
  end
end

class Stuff
  include EncryptedFields

  encrypted :ololo
  encrypted :new_name
end

EncryptedFields.instance_variable_get(:@encrypted_attributes)
=> {"Stuff"=>[:ololo, :new_name]}

此处不需要 class 个变量。模块实例变量就足够了。你没有任何继承,你实际上不能创建模块的实例,所以你总是只有一个地方定义 @encripted_attributes 变量,它将是你的 EncriptedFields 模块.

关于 class 变量和 class 实例变量之间的区别的好文章: http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/