2013-02-17 12 views
5

class_evalブロック内でクラス変数を定義するにはどうすればよいですか?私は、次のしている:User.methodsまたはPost.methodsを使用して検査中にモジュール#内の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を示しました。これは、class_evalのコンテキストで定義されているように意味があり、まさに私が必要とするものです。同様に、メソッド:findは個々のクラスのinstance_methodとして示されています。

しかし、私がクラス変数、すなわち@@collectionと考えていたのは、モジュールレベルのclass_variableであることが判明しました。 User.class_variablesまたはPost.class_variablesを調べると、空になります。しかしPersist.class_variables:@@collectionです。

これはどのように可能ですか? class_eval内のコンテキストがそのクラスのコンテキストではないのですか?したがって、変数@@collectionはモジュールではなくクラスで定義されるべきですか?

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

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

答えて

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:オブジェクトで初期化されていないクラス変数@@バー」につながります。ここで、指定されたブロックは、最上位の名前空間である "Object"のコンテキストにバインドされています。

+0

ありがとうございます!非常に明確な説明。私はclass_variable_setを使用して終了しました。 –

+1

ClassMethodsを使用すると、モジュール関連の静的変数も作成されます。 – Geoffroy

+0

この回答は修正する必要があります。 ClassMethodsモジュールを使用して最初に提示されたメソッドは、問題の問題を解決するためには機能しません。そこで宣言されたクラス変数もモジュールにスコープされ、すべてのクラスをまたいで共有されます。 'class_variable_set'メソッドは動作しますが、この回答の唯一の情報でなければなりません –

関連する問題