2011-06-02 49 views
1

我希望能够使传递给我的类方法的选项(可审计)可用于实例方法。我使用模块混合了类和实例方法。如何使用通过模块混入的类和实例方法中的类变量

最明显的选择是使用一个类变量,但在尝试访问时,我得到一个错误是:

在可审计

未初始化的类变量@@ 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现在包括解决方案)

+0

在版本在GitHub上只有指定当一个@在auditable_only_once前(线16),但你已经在代码中解决了这个问题。您是否使用该修复测试了代码?它仍然不起作用吗? – Jonathan 2011-06-02 13:42:30

+0

感谢您发现,我实际上尝试过单个和双个@,所以必须将更新版本的代码复制到Gist。现在修复。 – Kris 2011-06-03 15:15:03

回答

0

使用@@类实例变量不规则,严格要求时的次数极少。大多数情况下,他们似乎会造成麻烦或混乱。通常,您可以在类上下文中使用常规实例变量而不会出现问题。

你可能想要做的是为这种事情使用不同的模板。如果您拥有由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 
+1

请注意,我使用cattr_accessor(而不是mattr_accessor)来创建类属性(我在Rails中这样工作)并从我使用self.class.only_once的实例方法中访问它 – Kris 2011-06-14 17:44:59

相关问题