const_get но для переменных - PullRequest
1 голос
/ 11 июня 2010

Итак, я знаю, что вы можете сказать Kernel.const_get ("ClassName"), и вы получите обратно класс, которому соответствует строка в имени. Но как насчет переменных? Есть ли способ сделать:

test = "heyas"
some_method_here("test") #=> "heyas"

Большое спасибо

Дело в том, что мне это нужно в более сложном коде, реальный пример:

class User
  class Validations
    class << self
      def username_len
        return u.len > 3 and u.len < 22
      end
      # ...
    end
  end
  def validate(u,e,p)
    [:u, :e, :p].each do |v|
      Validations.send(:define_singleton_method, v, lambda { eval(v.to_s) }) # see this line
    end
    #other code to run validations
  end
end

Видите ли, лучшего способа нет, не так ли?

Ответы [ 4 ]

4 голосов
/ 11 июня 2010

Нет, есть только class_variable_get, instance_variable_get и const_get. Там нет local_variable_get. Но в любом случае это не имеет смысла: локальные переменные являются локальными по отношению к текущему телу метода, телу модуля, телу класса или телу скрипта, поэтому в конце концов они называются"локальными" переменными! Вы просто не можете получить к ним доступ другим способом.

Есть ли способ сделать:

test = "heyas"
some_method_here("test") #=> "heyas"

Нет, нет способа сделать это. И 1016 * не может быть способом сделать это. test является локальной переменной, что означает, что только существует в текущем теле скрипта. Оно не существует внутри тела some_method_here. В этом вся суть локальных переменных: вы никогда и ни при каких обстоятельствах не можете получить к ним доступ из других источников.

По поводу вашего комментария к другому ответу:

def my_func(str)
  var_a = 'hey'
  var_b = 'ah'
  return some_method_here(str)
end
#=> should return the corresponding variable's value, e.g. my_func('var_a')
#=> 'hey'

Опять же, это не может работать, так как весь смысл локальных переменных *1037* в том, что к ним невозможно получить доступ из других мест.

Но есть довольно простой вариант, который делает именно то, что вы хотите:

def my_func(str)
  {
    'var_a' => 'hey',
    'var_b' => 'ah'
  }[str]
end
#=> should return the corresponding variable's value, e.g. my_func('var_a')
#=> 'hey'

Это, конечно, можно еще упростить до:

my_func = {'var_a' => 'hey', 'var_b' => 'ah'}
#=> should return the corresponding variable's value, e.g. my_func['var_a']
#=> 'hey'

Учитывая, что вы можете передавать только ограниченное количество различных опций, вероятно, вместо них лучше использовать символы:

my_func = {var_a: 'hey', var_b: 'ah'}
#=> should return the corresponding variable's value, e.g. my_func[:var_a]
#=> 'hey'

То, что вы спрашиваете, в основном: передать ключ, получить значение. Это точно Что такое Hash.

РЕДАКТИРОВАТЬ: После пересмотренного вопроса, это лучшее, что я мог придумать:

def validate(u, e, p)
  local_variables.zip(local_variables.map {|var|
    eval(var.to_s)
  }).each {|var, val|
    Validations.send(:define_singleton_method, var) { val }
  }
end

Однако я думаю, что с дизайном что-то серьезно не так. Вы перезаписываете одноэлементных методов из User::Validations на основе разных экземпляров из User. По самому определению метода singleton в системе может быть только одна копия. Но у вас есть много разных экземпляров User, и каждый раз, когда вы звоните User#validate, он будет перезаписывать только копий User::Validations.u, User::Validations.e и User::Validations.p в в какой момент они начнут вести себя совершенно по-другому для всей системы .

Другими словами, вы меняете поведение всей системы , основываясь на единственном экземпляре . И может быть много экземпляров , и каждый раз поведение системы меняется.

Это просто не может быть правым.

u1 = User.new
u1.validate('u1', :e1, 1)

p User::Validations.u, User::Validations.e, User::Validations.p
# => 'u1'
# => :e1
# => 1

u2 = User.new
u2.validate('U2', :E2, 2.0)
# =>  warning: method redefined; discarding old u
# =>  warning: method redefined; discarding old e
# =>  warning: method redefined; discarding old p
#              ^^^^^^^^^^^^^^^^
#                    Oops!

p User::Validations.u, User::Validations.e, User::Validations.p
# => 'U2'
# => :E2
# => 2.0
#    ^^^
# Completely different results for the exact same arguments
3 голосов
/ 11 июня 2010

Может быть, я что-то упускаю, но почему вы не можете просто сделать test, чтобы получить его значение? Если вы определяете переменные экземпляра, существует instance_variable_get .

Редактировать: как упоминалось в моем комментарии ниже, вы можете сделать

def my_func(str); var_a="hey"; var_b="ah"; eval str; end
my_func('var_a') #=> "hey"

Но это кажется мне немного безумным, если не слишком безумным.

2 голосов
/ 11 июня 2010

попробуйте это:

def validate(u,e,p)
    [[:u, u], [:e, e], [:p, p]].each do |v|
        Validations.send(:define_singleton_method, v.first, lambda { v.last }) 
    end  
end

OR

def validate(u,e,p)
    {:u => u, :e => e, :p => p}.each do |k, v|
        Validations.send(:define_singleton_method, k, lambda { v }) 
    end  
end
1 голос
/ 07 декабря 2013

У привязки есть доступ к вашим локальным переменным, так что вы можете сделать это с привязкой # eval

eval("#{var_name}")

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

eval("#{var_name}") if eval("defined?(#{var_name})") == 'local-variable'

Например

test = 'heyas'
var_name = :test
eval("#{var_name}") if eval("defined?(#{var_name})") == 'local-variable' #=> "heyas"
var_name = :not_set
eval("#{var_name}") if eval("defined?(#{var_name})") == 'local-variable' #=> "nil"

На рубине ребра они определены в:

Binding#local_variable_defined?
Binding#local_variable_get
Binding#local_variable_set

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...