2012-02-27 4 views
16

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

+0

Как вам понравился ваш класс CS169? :) –

ответ

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 
+0

Это работает. Меня все еще путают различия между '@ count' и' @@ count' в методе 'class_eval'. Мое понимание - 1. Строка, переданная в class_eval, будет оцениваться во время выполнения - когда вызывается 'class Foo'. 2. '@ count' должен быть переменной экземпляра для всех экземпляров' Foo'. Зачем использовать переменную класса '@@ count', ведет ли она себя как ** переменная экземпляра **? Я знаю, что тема сложная, есть ли у вас какая-то ссылка, которую я мог бы прочитать? – steveyang

+0

это [сообщение в блоге] (http://martinfowler.com/bliki/ClassInstanceVariable.html) –

+0

Привет, @selmen. Я обнаружил, что '@@ count' действительно не ведет себя как переменная экземпляра. Он ведет себя как переменная класса. Это не то, что я ожидал – steveyang

Смежные вопросы