如何在class_eval块中定义类变量?我有以下内容:
module Persist
def self.included(base)
# base is the class including this module
base.class_eval do
# class context begin
@@collection = Connection.new.db('nameofdb').collection(self.to_s.downcase)
def self.get id # Class method
#...
end
end
end
# Instance methods follow
def find
@@collection.find().first
#...
end
end
class User
include Persist
end
class Post
include Persist
end
当使用:get
或User.methods
进行内省时,用户和帖子类都显示Post.methods
。这是有道理的,因为它们是在class_eval的上下文中定义的,正是我所需要的。类似地,方法:find
显示为各个类的instance_method。
但是,我认为是类变量,即@@collection
,结果是模块级class_variable。当我反省User.class_variables
或Post.class_variables
时,它们会变空。但Persist.class_variables
显示:@@collection
。
这怎么可能?不是class_eval
块中的上下文。那么变量@@collection
不应该在类而不是模块上定义吗?
此外,@@collection
的值始终是包含它的最后一个类的名称。因此,在这种情况下,它始终是“帖子”而不是“用户”。我认为这是因为它是一个模块级变量,它会在每个包含上发生变化。这是对的吗?
最后,我将如何在此上下文中定义一个类变量,以便每个类都有自己的@@collection
定义。
答案 0 :(得分:5)
一种方法是为类变量创建访问器方法。
module Persist
def self.included(base)
# Adds class methods from 'ClassMethods' to including class.
base.extend(ClassMethods)
base.class_eval do
self.collection = Connection.new.db('nameofdb').collection(self.to_s.downcase)
# ...
end
end
module ClassMethods
def collection=(value)
@@collection = value
end
def collection
@@collection
end
end
# Instance methods follow
def find
self.class.collection.find().first
#...
end
end
class User
include Persist
end
class Post
include Persist
end
另一种方法是通过访问器 class_variable_set 等访问模块中包含类的类变量。
def self.included(base)
base.class_eval do
class_variable_set('@@collection', Connection.new.db('nameofdb').collection(self.to_s.downcase))
# …
end
end
我会回答你的问题“这怎么可能?不是class_eval块中的上下文。”
class_eval 方法确实使 self 引用在给定块内调用的类。这允许您调用类方法等。但是,类变量将在块绑定的上下文中进行计算 - 这里是模块。
例如,尝试这样做:
class Foo
@@bar = 1
end
Foo.class_eval { puts @@bar }
这将导致异常“NameError:未初始化的类变量@@ bar in Object”。这里给定的块绑定到“对象”的上下文,即顶级命名空间。