Какова область видимости переменной в строке `class_eval`?

Я использую 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 выполняет следующую строку?

16
задан steveyang 28 February 2012 в 04:47
поделиться