如何使用通过模块混合的类和实例方法中的类变量
我希望能够将传递给我的类方法(可审核)的选项可供实例方法使用。我使用模块混合类和实例方法。
显而易见的选择是使用类变量,但在尝试访问它时出现错误:
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 现在包含解决方案)
I want to be able to make an option passed to my class method (auditable) available to instance methods. I'm mixing in both the class and instance methods using a Module.
The obvious choice is to use a class variable, but I get an error when trying access it:
uninitialized class variable @@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
I've stripped out some of the code to make this a bit easier to read, the full code is here: https://gist.github.com/1004399 (EDIT: Gist now includes the solution)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
使用
@@
类实例变量是不规则的,并且严格要求使用它们的情况非常罕见。大多数时候,它们似乎只是造成麻烦或混乱。一般来说,您可以在类上下文中使用常规实例变量而不会出现问题。您可能想要做的是对此类事情使用不同的模板。如果您有由 ActiveSupport 提供的
mattr_accessor
,您可能希望使用它来代替该变量,或者您始终可以在ClassMethods
组件中编写自己的等效项。我使用的一种方法是将扩展分解为两个模块:一个钩子和一个实现。该钩子仅将方法添加到基类中,如果需要,可用于添加其余方法,但不会污染命名空间:
这个
engage
方法可以被称为任何你喜欢的方法,正如您的示例中所示,它是可审计的
。接下来,您为扩展在执行时添加的类和实例方法创建一个容器模块:
在本例中,有一个简单的方法
class_extender_options
,可用于查询或修改特定选项的选项班级。这避免了直接使用实例变量。还添加了示例实例方法。你可以定义一个简单的例子:
然后测试它是否正常工作:
Using
@@
class instance variables is irregular and the number of of occasions when they're strictly required is exceedingly rare. Most of the time they just seem to cause trouble or confusion. Generally you can use regular instance variables in the class context without issue.What you might want to do is use a different template for this sort of thing. If you have
mattr_accessor
, which is provided by ActiveSupport, you may want to use that instead of that variable, or you can always write your own equivalent in yourClassMethods
component.One approach I've used is to break up your extension into two modules, a hook and an implementation. The hook only adds the methods to the base class that can be used to add the rest of the methods if required, but otherwise doesn't pollute the namespace:
This
engage
method can be called anything you like, as in your example it isauditable
.Next you create a container module for the class and instance methods that the extension adds when it is exercised:
In this case there is a simple method
class_extender_options
that can be used to query or modify the options for a particular class. This avoids having to use the instance variable directly. An example instance method is also added.You can define a simple example:
Then test that it is working properly: