我希望能够将一个选项传递给实例方法可用的类方法(可审计)。我正在使用Module混合类和实例方法。
显而易见的选择是使用类变量,但在尝试访问时遇到错误:
未审计的类变量@@ auditable_only_once in Auditable
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现在包含解决方案)
答案 0 :(得分:0)
使用@@
类实例变量是不规则的,严格要求的次数极少。大多数时候他们似乎只会造成麻烦或困惑。通常,您可以在类上下文中使用常规实例变量而不会出现问题。
您可能想要做的是为这种事情使用不同的模板。如果您有mattr_accessor
,它由ActiveSupport提供,您可能希望使用该变量而不是该变量,或者您始终可以在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