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

ˆᵛˆ*_*ˆᵛˆ 5 ruby mongodb

如何在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
Run Code Online (Sandbox Code Playgroud)

User和Post类都显示: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定义.

Ric*_*ond 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
Run Code Online (Sandbox Code Playgroud)

另一种方法是通过访问器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
Run Code Online (Sandbox Code Playgroud)

我会回答你的问题"这怎么可能?不是class_eval块中的上下文."

class_eval方法确实使自我是指它被调用给定块内的类.这允许您调用类方法等.但是,类变量将在块绑定的上下文中进行计算 - 这里是模块.

例如,尝试这样做:

class Foo
    @@bar = 1
end

Foo.class_eval { puts @@bar }
Run Code Online (Sandbox Code Playgroud)

这将导致异常"NameError:未初始化的类变量@@ bar in Object".这里给定的块绑定到顶层命名空间"Object"的上下文.