在Module#内包含的class_eval中定义类变量

问题描述:

如何在class_eval块内定义类变量?我有以下几点:在Module#内包含的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.methodsPost.methods反思的时候。这是有道理的,因为它们是在class_eval的上下文中定义的,正是我所需要的。类似地,方法:find被显示为各个类的实例_方法。

但是,我认为是一个类变量,即@@collection,原来是一个模块级别的class_variable。当我反思User.class_variablesPost.class_variables时,他们变成空的。然而Persist.class_variables显示:@@collection

这怎么可能? class_eval块内的上下文不是该类的上下文。所以变量@@collection不应该定义在类而不是模块?

而且,@@collection的值始终是包含它的最后一个类的名称。所以在这种情况下,它始终是“帖子”,而不是“用户”。我认为这是因为它是一个模块级别的变量,它会在每个包含中改变。它是否正确?

最后,我将如何在此上下文中定义一个类变量,以便每个类具有它自己的@@collection定义。

一种方法是为类变量创建存取方法。

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方法确实让自我指它被调用给定块内的类。这允许您调用类方法等。但是,类变量将在块绑定的上下文中进行评估 - 在此处为模块。

例如,尝试这样做:

class Foo 
    @@bar = 1 
end 

Foo.class_eval { puts @@bar } 

这将导致异常“NameError:未初始化的类变量@@的对象吧。”这里给定的块绑定到顶层名称空间“对象”的上下文。

+0

谢谢!一个非常明确的解释。我结束了使用class_variable_set。使用ClassMethods的 – 2013-02-18 12:14:10

+1

也创建模块相关的静态变量... – Geoffroy 2014-02-13 22:40:24

+0

这个答案需要修复。首先使用ClassMethods模块呈现的方法不能解决问题的问题。在那里声明的类变量也被限制在模块中,因此在所有包含的类中共享。 'class_variable_set'方法可以工作,并且应该是这个答案中唯一的信息 – 2014-04-30 02:22:35