16

class_eval現在のクラスのコンテキストで実行されるコードを記述するために使用しています。次のコードでは、属性値の変更に対するカウンターを追加します。

class Class
  def attr_count(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name # create the attribute's getter
    class_eval %Q{
      @count = 0
      def #{attr_name}= (attr_name)
        @attr_name = attr_name
        @count += 1
      end

      def #{attr_name}
        @attr_name
      end
    }
    end
  end
class Foo
  attr_count :bar
end

f = Foo.new
f.bar = 1

私の理解では、ランタイム クラスのコンテキストでclass_evalブロックが評価されるということです。私の場合は、. 上記のコードは次のように実行されると思います。class Foo

class Foo
  attr_count :bar
  @count = 0
  def bar= (attr_name)
    @attr_name = attr_name
    @count += 1
  end

  def bar
    @attr_name
  end
end

ただし、上記のコードは、エラーが原因であるというエラーが発生しました@count += 1。なぜスーパー@countになっているのかわかりませんか?nil:NilClass

(eval):5:in `bar=': undefined method `+' for nil:NilClass (NoMethodError)

一方、@selman は@countインスタンス メソッド内に代入を配置するソリューションを提供しており、それは機能します。

class Class
  def attr_count(attr_name)
    #...
    class_eval %Q{
      def #{attr_name}= (attr_name)
        @attr_name = attr_name
        if @count
          @count += 1
        else
          @count = 1
        end
      end
      #...
    }
  end
end

変数スコープの変更が機能するのはなぜですか? 次の文字列をどのようにclass_eval実行しますか?

4

1 に答える 1

12

class_eval程度ではない程度@countです。この変数をクラス レベルで定義すると、class instance variableではなく になりますinstance variable

class Class
  def attr_count(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name # create the attribute's getter
    class_eval %Q{
      def #{attr_name}= (attr_name)
        @attr_name = attr_name
        if @count
          @count += 1
        else
          @count = 1
        end
      end

      def #{attr_name}
        @attr_name
      end
    }
  end
end

class Foo
  attr_count :bar
end

f = Foo.new
f.bar = 1
于 2012-02-27T14:47:18.047 に答える