2013-08-08 3 views
1

Я ищу в чей-то код и обнаружил, что он сделал класс Eval с чем-то вроде этогоНужна помощь, чтобы понять кусок кода

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

, а затем к ней обращаются как этот

self.class_eval("@default_robot_engine") 

Мне нужна помощь, чтобы понять этот код. Есть ли другой способ доступа к @default_robot_engine, а не к классу class_eval?

когда я 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“]

и я в состоянии получить доступ ко всем переменную экземпляра, как этот ClassName.registered_robot_engine кроме default_robot_engine. Зачем?

Ok я получил ответ, потому что эта переменная экземпляра является динамичным и attr_reader не установлен на нем, так что я думаю, что единственный способ получить доступ к нему осуществляется через class_eval

+6

условия: 'instance_variable_get()', 'instance_variable_set()', 'attr_accessor' – Zabba

+0

Привет @Zabba Я обновил свой запрос. можете ли вы заглянуть в нее? –

+0

@Zabba: Точно. Я написал ответ, прежде чем читать ваш комментарий, но +1 в любом случае. –

ответ

0

Я могу получить доступ ко всем переменную экземпляра, как этот ClassName.registered_robot_engine кроме default_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) 

Основное правило в рубина, что все переменные экземпляра являются частными по умолчанию, так что если вы не предоставите методы доступа к переменной экземпляра, вы не можете получить доступ к нему. В приведенном выше примере нет методов доступа, определенных для @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 

Во-вторых, трудно себе представить, почему программист не использовал стандартный геттер и сеттер, а в:

class << self 
    attr_accessor :default_robot_engine 
end 

Я предположил бы, что программист использует чужой модуль, и программист решил нарушить инкапсуляцию, что рубин позволяет делать. Некоторые языки считают, что хотя инкапсуляция хороша, ее не следует строго выполнять. Если по какой-то причине программист хочет нарушить инкапсуляцию, у них должна быть свобода.

0

чтения Try и понимание этого один:

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

Это очень полезно.

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 "[email protected][email protected]@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 "@[email protected]@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, он печатает вещи, которые выглядят как переменные класса. Это только @@ c из трех переменных, определенных в строке 094.

Так что я предполагаю, что этот self.class_eval является способом определения переменной класса только с одним @ вместо двух.

Я не знаю, почему a + @ b + @@ c не удается найти a, но @b + @@ c находит обе переменные. Поэтому я думаю, что это лишь частичный ответ ... Я не знаю точно, будет ли @b храниться в другом месте, чем @@ c, и я не знаю, что происходит с.

Это может быть просто странность Руби.

+0

'Я не знаю, почему a + @ b + @@ c не может найти'. Потому что переменные экземпляра присоединяются к тому или иному объекту, когда они определены; локальных переменных нет. Запись 'class_eval 'a = 3" 'не оказывает долгосрочного воздействия. – 7stud

+0

Спасибо, 7stud. Делает совершенный смысл. – ChrisPhoenix

2

Это особенно странный фрагмент кода. Во-первых, self.class_eval не требуется вообще.Обычная class_eval будет делать только право. Я предполагаю, что программист использовался больше на других языках, чем Ruby. В Ruby один использует явный приемник self только в редких случаях, например при вызове методов, заканчивающихся знаком =, или при проверке того, что вызванный метод является общедоступным методом (частные методы не будут выполняться при вызове с явным получателем).

Во-вторых, трудно себе представить, почему программист не использовать стандартные методы получения и установки, как в:

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, buyt с помощью #instance_variable_get/set методы:

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

Класса Eval кажется мне слишком большим молотком для этого случая.

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