【问题标题】:How do I use class variables from class and instance methods which are mixed in via a Module如何使用通过模块混合的类和实例方法中的类变量
【发布时间】:2011-06-02 13:19:46
【问题描述】:

我希望能够使传递给我的类方法(可审计)的选项可用于实例方法。我正在使用模块混合类和实例方法。

显而易见的选择是使用类变量,但尝试访问它时出现错误:

在 Auditable 中未初始化的类变量 @@auditable_only_once

class Document
  include Auditable
  auditable :only_once => true
end

# The mixin
module Auditable
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def auditable(options = {})

      options[:only_once] ||= false

      class_eval do
        # SET THE OPTION HERE!!
        @@auditable_only_once = options[:only_once]
      end
      end
    end

    private

    def audit(action)
      # AND READ IT BACK LATER HERE
      return if @@auditable_only_once && self.audit_item
      AuditItem.create(:auditable => self, :tag => "#{self.class.to_s}_#{action}".downcase, :user => self.student)
    end    
  end

我删除了一些代码以使其更易于阅读,完整代码在这里:https://gist.github.com/1004399(编辑:Gist 现在包含解决方案)

【问题讨论】:

  • 在 github 上的版本中,分配时(第 16 行)在 auditable_only_once 前面只有一个 @,但您已在此处的代码中修复了该问题。您是否使用该修复程序测试了代码?还是不行吗?
  • 感谢您发现这一点,我实际上尝试了单@和双@,因此必须将较新版本的代码复制到 Gist。现已修复。

标签: ruby class-variables


【解决方案1】:

使用@@ 类实例变量是不规则的,并且严格要求它们的场合非常少见。大多数时候,它们似乎只是造成麻烦或混乱。通常,您可以在类上下文中使用常规实例变量而不会出现问题。

您可能想要对这类事情使用不同的模板。如果您有 ActiveSupport 提供的 mattr_accessor,您可能希望使用它而不是那个变量,或者您可以随时在您的 ClassMethods 组件中编写自己的等效项。

我使用的一种方法是将扩展分为两个模块,一个钩子和一个实现。该钩子仅将方法添加到可用于在需要时添加其余方法的基类中,否则不会污染命名空间:

module ClassExtender
  def self.included(base)
    base.send(:extend, self)
  end

  def engage(options = { })
    extend ClassExtenderMethods::ClassMethods
    include ClassExtenderMethods::InstanceMethods

    self.class_extender_options.merge!(options)
  end
end

这个engage 方法可以调用任何你喜欢的方法,在你的例子中它是auditable

接下来,为扩展在执行时添加的类和实例方法创建一个容器模块:

module ClassExtenderMethods
  module ClassMethods
    def class_extender_options
      @class_extender_options ||= {
        :default_false => false
      }
    end
  end

  module InstanceMethods
    def instance_method_example
      :example
    end
  end
end

在这种情况下,有一个简单的方法class_extender_options 可用于查询或修改特定类的选项。这避免了必须直接使用实例变量。还添加了一个示例实例方法。

你可以定义一个简单的例子:

class Foo
  include ClassExtender

  engage(:true => true)
end

然后测试它是否正常工作:

Foo.class_extender_options
# => {:default_false=>false, :true=>true}

foo = Foo.new
foo.instance_method_example
# => :example

【讨论】:

  • 请注意,我使用 cattr_accessor(不是 mattr_accessor)来创建类属性(我在 Rails 中,所以这有效)并从我使用 self.class.only_once 的实例方法访问它
猜你喜欢
  • 1970-01-01
  • 2017-06-18
  • 2014-03-22
  • 1970-01-01
  • 2011-10-03
  • 1970-01-01
  • 2015-02-13
  • 2017-12-27
  • 1970-01-01
相关资源
最近更新 更多