`class_eval`字符串中的变量范围是什么?

ste*_*ang 16 ruby metaprogramming 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
Run Code Online (Sandbox Code Playgroud)

我的理解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
Run Code Online (Sandbox Code Playgroud)

但是上面的代码导致错误说,错误是由@count += 1.我想不通为什么@countnil:NilClass它的超?

(eval):5:in `bar=': undefined method `+' for nil:NilClass (NoMethodError)
Run Code Online (Sandbox Code Playgroud)

另一方面,@ serman给出了一个解决方案,@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
Run Code Online (Sandbox Code Playgroud)

为什么更改变量范围有效?如何class_eval执行以下字符串?

Sel*_*lug 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
Run Code Online (Sandbox Code Playgroud)