What's the variable scope within `class_eval` string?

风流意气都作罢 提交于 2019-12-03 05:06:29

问题


I am using class_eval to write code to be executed under the context of current class. In the following code, I want to add a counter for changes of attribute values.

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

My understanding of class_eval is that it evaluates the block in the context of the runtime class - in my case, under class Foo. I expect the above code runs similar as:

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

  def bar
    @attr_name
  end
end

However the above code resulted in error saying, the error is caused by @count += 1. I cannot figure out why @count has nil:NilClass as its super?

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

On the other hand, @selman has given a solution to put @count assignment within the instance method and it works.

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

Why changes the variable scope works? How does class_eval execute its following string?


回答1:


it is not about class_eval it is about @count. if you define this variable at class level it will be a class instance variable not an 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


来源:https://stackoverflow.com/questions/9466624/whats-the-variable-scope-within-class-eval-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!