1

I am looking into someones code and found that he has done class eval with something like this

self.class_eval("@default_robot_engine = RobotEngine.new(some_block)")

and later it is accessed like this

self.class_eval("@default_robot_engine")

I need help to understand this code. Is there any other way to access @default_robot_engine rather than doing class_eval on it?

when I do Class.instance_variable_names I get

["@attribute_methods_mutex", "@generated_attribute_methods", "@generated_feature_methods", "@observer_instances", "@per_page", "@parent_name", "@registered_robot_engines", "@default_robot_engine", "@primary_key", "@quoted_primary_key", "@locking_column", "@attribute_methods_generated", "@table_name", "@quoted_table_name", "@arel_table", "@arel_engine", "@relation", "@columns", "@column_names", "@columns_hash", "@cached_attributes", "@attribute_method_matchers_cache", "@generated_external_attribute_methods"]

and I am able to access all the instance variable like this ClassName.registered_robot_engine except default_robot_engine. why?

Ok I got the answer because this instance variable is a dynamic one and attr_reader is not set on it so I think only way to access it is via class_eval

4

4 回答 4

2

这是一段特别奇怪的代码。首先,self.class_eval完全没有必要。平原class_eval会做得恰到好处。我猜这个程序员比 Ruby 更习惯于其他语言。在 Ruby 中,self仅在极少数情况下使用显式接收器,例如在调用以=符号结尾的方法时,或者在确保调用的方法是公共方法时(使用显式接收器调用私有方法时会失败)。

其次,很难想象程序员为什么不使用标准的 getter 和 setter,如下所示:

class << self
  attr_accessor :default_robot_engine
end

# Here is the case when its legal to use explicit self receiver:
self.default_robot_engine = RobotEngine.new( some_block )

然后只需通过

default_robot_engine

我强烈怀疑原始程序员对 Ruby 基础知识的无知。尽管有时有理由在不定义访问器的情况下篡改实例变量,但最好不要通过class_eval使用#instance_variable_get/set方法来通过、购买:

instance_variable_set :@default_robot_engine, RobotEngine.new( some_block )
instance_variable_get :@default_robot_engine

在我看来,类 eval 对这种情况来说太大了。

于 2013-08-08T07:59:50.510 回答
1

哇,这是一个有趣的。

1.9.3-p429 :094 > class C; self.class_eval "a=3;@b=4;@@c=5"; end
 => 5 
1.9.3-p429 :095 > C.class_variables
 => [:@@c] 
1.9.3-p429 :096 > class C; puts self.class_eval "a+@b+@@c"; end
NameError: undefined local variable or method `a' for C:Class
from (irb):96:in `class_eval'
from (irb):96:in `class_eval'
from (irb):96:in `<class:C>'
from (irb):96
from /Users/cphoenix/.rvm/rubies/ruby-1.9.3-p429/bin/irb:16:in `<main>'
1.9.3-p429 :097 > class C; puts self.class_eval "@b+@@c"; end
9
 => nil 
1.9.3-p429 :098 > 
1.9.3-p429 :098 > C.object_id
 => 2151815060 
1.9.3-p429 :099 > C.class_eval "puts self.object_id"
2151815060
 => nil 
1.9.3-p429 :100 > 

这就是似乎正在发生的事情。当您执行 C.class_eval 时,您是在类的上下文中执行代码;自我是班级。

当你说 C.class_variables 时,它会打印出看起来像类变量的东西。这只是我在第 094 行定义的三个变量中的 @@c。

所以我猜这个 self.class_eval 是一种定义类变量的方法,只有一个 @ 而不是两个。

我不知道为什么a+@b+@@c 找不到a,但是@b+@@c 确实找到了这两个变量。所以我想这只是部分答案......我不确定@b是否存储在与@@c不同的地方,我不知道a会发生什么。

这可能只是 Ruby 的怪癖。

于 2013-08-08T07:47:21.533 回答
0

除了 default_robot_engine 之外,我可以访问所有像 ClassName.registered_robot_engine 这样的实例变量。为什么?

class Dog
  class<< self
    attr_accessor :registered_robot_engine

    def set_stuff
      @registered_robot_engine = 'hello'
      @default_robot_engine = 20
    end
  end
end

Dog.set_stuff
puts Dog.registered_robot_engine
puts Dog.default_robot_engine

--output:--
hello
1.rb:16:in `<main>': undefined method `default_robot_engine' for Dog:Class (NoMethodError)

ruby 的基本规则是默认情况下所有实例变量都是私有的,因此除非您为实例变量提供访问器方法,否则您无法访问它。在上面的示例中,没有为@default_robot_engine 定义访问器方法,因此它是不可访问的,而另一个实例变量确实为它定义了访问器方法,因此它是可访问的。

class_eval() 和 instance_eval() 都允许您违反封装并读取或写入私有实例变量:

class Dog
  class <<self
    def set_stuff
      @registered_robot_engine = 'hello'
      @default_robot_engine = 20
    end

    def set_more_stuff
      class_eval do 
        @default_robot_engine = 100 
      end
    end
  end
end

Dog.set_stuff
Dog.set_more_stuff

puts Dog.class_eval{ @default_robot_engine }

--output:--
100

instance_variable_set() 和 instance_variable_get() 允许你做同样的事情:

class Dog
  def initialize
    @name = "Rover"
  end
end

d = Dog.new
d.instance_variable_set(:@name, "John")
puts d.instance_variable_get(:@name)

--output:--
John

其次,很难想象程序员为什么不使用标准的 getter 和 setter,如下所示:

class << self
  attr_accessor :default_robot_engine
end

我猜程序员正在使用其他人的模块,并且程序员决定违反封装,而 ruby​​ 允许您这样做。一些语言认为,虽然封装很好,但不应该严格执行。如果出于某种原因程序员想要违反封装,他们应该有这样做的自由。

于 2013-08-08T08:39:37.270 回答
0

尝试阅读并理解这一点:

http://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval

这是非常有用的。

于 2013-08-08T07:18:31.837 回答