Какая область переменных в строке `class_eval`? - программирование
Подтвердить что ты не робот

Какая область переменных в строке `class_eval`?

Я использую class_eval для записи кода, который будет выполняться в контексте текущего класса. В следующем коде я хочу добавить счетчик для изменения значений атрибутов.

class Class
  def attr_count(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name # create the attribute 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 выполняет следующую строку?

4b9b3361

Ответ 1

речь идет не о class_eval о @count. если вы определяете эту переменную на уровне класса, она будет class instance variable not a instance variable.

class Class
  def attr_count(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name # create the attribute 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