I am looking into someones code and found that he has done class eval with something like this
我正在研究某些代码并发现他已经用这样的东西做了类eval
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?
我需要帮助来理解这段代码。有没有其他方法可以访问@default_robot_engine而不是在其上执行class_eval?
when I do Class.instance_variable_names I get
当我做Class.instance_variable_names我得到
["@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"]
[“@attribute_methods_mutex”,“@ generated_attribute_methods”,“@ generate_feature_methods”,“@ observer_instances”,“@ per_page”,“@ parent_name”,“@ register_robot_engines”,“@ default_robot_engine”,“@ primary_key”,“@ quoted_primary_key” ,“@ locking_column”,“@ attribute_methods_generated”,“@ table_name”,“@ quoted_table_name”,“@ arel_table”,“@arel_engine”,“@ alllation”,“@ column”,“@ column_names”,“@ column_hash” ,“@ cache_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?
并且我能够访问除了default_robot_engine之外的所有实例变量,如ClassName.registered_robot_engine。为什么?
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
好的,我得到了答案,因为这个实例变量是一个动态变量,并且没有设置attr_reader所以我认为只有通过class_eval才能访问它
4 个解决方案
#1
0
I am able to access all the instance variable like this ClassName.registered_robot_engine except default_robot_engine. why?
我可以访问除了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)
The basic rule in ruby is that all instance variables are private by default, so unless you provide accessor methods for an instance variable you can't access it. In the example above, there are no accessor methods defined for @default_robot_engine, so it is inaccessible, while the other instance variable does have accessor methods defined for it, so it is accessible.
ruby中的基本规则是默认情况下所有实例变量都是私有的,因此除非为实例变量提供访问器方法,否则无法访问它。在上面的示例中,没有为@default_robot_engine定义访问器方法,因此它不可访问,而另一个实例变量确实具有为其定义的访问器方法,因此可以访问它。
Both class_eval() and instance_eval() allow you to violate encapsulation and read or write private instance variables:
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() and instance_variable_get() allow you to do the same thing:
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
Secondly, it is hard to imagine why the programmer didn't use standard getter and setter, as in:
其次,很难想象程序员为什么不使用标准的getter和setter,如:
class << self attr_accessor :default_robot_engine end
I would guess that the programmer is using someone else's module, and the programmer has decided to violate encapsulation, which ruby allows you to do. Some languages believe that although encapsulation is good, it shouldn't be strictly enforced. If for some reason a programmer wants to violate encapsulation, they should have the freedom to do so.
我猜想程序员正在使用其他人的模块,程序员决定违反封装,ruby允许你这样做。有些语言认为虽然封装很好,但不应严格执行。如果由于某种原因,程序员想要违反封装,他们应该有*这样做。
#2
2
This is a particularly weird piece of code. Firstly, self.class_eval
is not necessary at all. Plain class_eval
would do just right. I guess that the programmer was used more to other languages than Ruby. In Ruby, one uses explicit self
receiver only in rare cases, such as when invoking methods ending with =
sign, or when making sure that the method called is a public method (private methods will fail when called with explicit receiver).
这是一段特别奇怪的代码。首先,self.class_eval根本不是必需的。普通的class_eval会做得恰到好处。我想程序员更多地使用其他语言而不是Ruby。在Ruby中,仅在极少数情况下使用显式自我接收器,例如在调用以=符号结尾的方法时,或者确保调用的方法是公共方法时(私有方法在使用显式接收器调用时将失败)。
Secondly, it is hard to imagine why the programmer didn't use standard getter and setter, as in:
其次,很难想象程序员为什么不使用标准的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 )
and later access it simply by
然后简单地访问它
default_robot_engine
I strongly suspect the original programmer from ignorance of Ruby basics. Even though one sometimes has reasons to tamper instance variables without defining accessors, one does it preferrably not via class_eval
, buyt by using #instance_variable_get/set
methods:
我强烈怀疑原来的程序员是因为忽略了Ruby的基础知识。即使有人有理由在不定义访问器的情况下篡改实例变量,但最好不要通过class_eval,buyt使用#instal_variable_get / set方法:
instance_variable_set :@default_robot_engine, RobotEngine.new( some_block )
instance_variable_get :@default_robot_engine
Class eval seems to me like too big a hammer for this case.
对于这种情况,类似于对我来说似乎太大了。
#3
1
Wow, this is a fun one.
哇,这是一个有趣的。
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 >
Here's what seems to be happening. When you do C.class_eval, you are executing the code in the context of the class; self is the class.
这是似乎正在发生的事情。当您执行C.class_eval时,您正在该类的上下文中执行代码;自我是阶级。
When you say C.class_variables, it prints out the things that look like class variables. That's only @@c out of the three variables I defined in line 094.
当你说C.class_variables时,它打印出看起来像类变量的东西。这只是我在第094行定义的三个变量中的@@ c。
So I'm guessing that this self.class_eval is a way of defining a class variable with only one @ instead of two.
所以我猜这个self.class_eval是一种只用一个@而不是两个来定义一个类变量的方法。
I don't know why a+@b+@@c fails to find a, but @b+@@c does find both variables. So I guess this is only a partial answer... I don't know for sure whether @b is stored in a different place than @@c, and I have no clue what happens to a.
我不知道为什么+ @ b + @@ c找不到,但@b + @@ c确实找到了这两个变量。所以我想这只是一个部分答案......我不确定@b是否存储在与@@ c不同的地方,我不知道发生了什么。
This may just be Ruby weirdness.
这可能只是Ruby的怪异。
#4
0
Try reading and understanding this one:
尝试阅读并理解这个:
http://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval
It is very useful.
这非常有用。
#1
0
I am able to access all the instance variable like this ClassName.registered_robot_engine except default_robot_engine. why?
我可以访问除了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)
The basic rule in ruby is that all instance variables are private by default, so unless you provide accessor methods for an instance variable you can't access it. In the example above, there are no accessor methods defined for @default_robot_engine, so it is inaccessible, while the other instance variable does have accessor methods defined for it, so it is accessible.
ruby中的基本规则是默认情况下所有实例变量都是私有的,因此除非为实例变量提供访问器方法,否则无法访问它。在上面的示例中,没有为@default_robot_engine定义访问器方法,因此它不可访问,而另一个实例变量确实具有为其定义的访问器方法,因此可以访问它。
Both class_eval() and instance_eval() allow you to violate encapsulation and read or write private instance variables:
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() and instance_variable_get() allow you to do the same thing:
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
Secondly, it is hard to imagine why the programmer didn't use standard getter and setter, as in:
其次,很难想象程序员为什么不使用标准的getter和setter,如:
class << self attr_accessor :default_robot_engine end
I would guess that the programmer is using someone else's module, and the programmer has decided to violate encapsulation, which ruby allows you to do. Some languages believe that although encapsulation is good, it shouldn't be strictly enforced. If for some reason a programmer wants to violate encapsulation, they should have the freedom to do so.
我猜想程序员正在使用其他人的模块,程序员决定违反封装,ruby允许你这样做。有些语言认为虽然封装很好,但不应严格执行。如果由于某种原因,程序员想要违反封装,他们应该有*这样做。
#2
2
This is a particularly weird piece of code. Firstly, self.class_eval
is not necessary at all. Plain class_eval
would do just right. I guess that the programmer was used more to other languages than Ruby. In Ruby, one uses explicit self
receiver only in rare cases, such as when invoking methods ending with =
sign, or when making sure that the method called is a public method (private methods will fail when called with explicit receiver).
这是一段特别奇怪的代码。首先,self.class_eval根本不是必需的。普通的class_eval会做得恰到好处。我想程序员更多地使用其他语言而不是Ruby。在Ruby中,仅在极少数情况下使用显式自我接收器,例如在调用以=符号结尾的方法时,或者确保调用的方法是公共方法时(私有方法在使用显式接收器调用时将失败)。
Secondly, it is hard to imagine why the programmer didn't use standard getter and setter, as in:
其次,很难想象程序员为什么不使用标准的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 )
and later access it simply by
然后简单地访问它
default_robot_engine
I strongly suspect the original programmer from ignorance of Ruby basics. Even though one sometimes has reasons to tamper instance variables without defining accessors, one does it preferrably not via class_eval
, buyt by using #instance_variable_get/set
methods:
我强烈怀疑原来的程序员是因为忽略了Ruby的基础知识。即使有人有理由在不定义访问器的情况下篡改实例变量,但最好不要通过class_eval,buyt使用#instal_variable_get / set方法:
instance_variable_set :@default_robot_engine, RobotEngine.new( some_block )
instance_variable_get :@default_robot_engine
Class eval seems to me like too big a hammer for this case.
对于这种情况,类似于对我来说似乎太大了。
#3
1
Wow, this is a fun one.
哇,这是一个有趣的。
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 >
Here's what seems to be happening. When you do C.class_eval, you are executing the code in the context of the class; self is the class.
这是似乎正在发生的事情。当您执行C.class_eval时,您正在该类的上下文中执行代码;自我是阶级。
When you say C.class_variables, it prints out the things that look like class variables. That's only @@c out of the three variables I defined in line 094.
当你说C.class_variables时,它打印出看起来像类变量的东西。这只是我在第094行定义的三个变量中的@@ c。
So I'm guessing that this self.class_eval is a way of defining a class variable with only one @ instead of two.
所以我猜这个self.class_eval是一种只用一个@而不是两个来定义一个类变量的方法。
I don't know why a+@b+@@c fails to find a, but @b+@@c does find both variables. So I guess this is only a partial answer... I don't know for sure whether @b is stored in a different place than @@c, and I have no clue what happens to a.
我不知道为什么+ @ b + @@ c找不到,但@b + @@ c确实找到了这两个变量。所以我想这只是一个部分答案......我不确定@b是否存储在与@@ c不同的地方,我不知道发生了什么。
This may just be Ruby weirdness.
这可能只是Ruby的怪异。
#4
0
Try reading and understanding this one:
尝试阅读并理解这个:
http://www.jimmycuadra.com/posts/metaprogramming-ruby-class-eval-and-instance-eval
It is very useful.
这非常有用。