5

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

UserとPostの両方のクラスは、または:getを使用して内省するときに表示されます。class_evalのコンテキストで定義されており、まさに私が必要としているものであるため、これは理にかなっています。同様に、メソッドは個々のクラスのinstance_methodとして表示されます。User.methodsPost.methods:find

しかし、私が思ったのはクラス変数、つまり@@collection、モジュールレベルのclass_variableであることがわかりました。私が内省するか、User.class_variablesまたはPost.class_variables、それらは空になります。ただしPersist.class_variables、を示しています:@@collection

これはどのように可能ですか?class_evalブロック内のコンテキストはクラスのコンテキストではありません。では、変数@@collectionはモジュールではなくクラスで定義されるべきではありませんか?

また、の値@@collectionは常に、それを含む最後のクラスの名前です。したがって、この場合、それは常に「投稿」であり、「ユーザー」ではありません。これはモジュールレベルの変数であるためだと思います。インクルードごとに変更されます。これは正しいです?

最後に、各クラスが独自の定義を持つように、このコンテキストでクラス変数をどのように定義しますか@@collection

4

1 に答える 1

5

1 つの方法は、クラス変数のアクセサ メソッドを作成することです。

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: uninitialized class variable @@bar in Object」という例外が発生します。ここで、指定されたブロックは、最上位の名前空間である「オブジェクト」のコンテキストにバインドされます。

于 2013-02-17T14:55:41.720 に答える