const_get但是对于变量

时间:2010-06-10 23:53:50

标签: ruby

所以我知道你可以说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 个答案:

答案 0 :(得分:4)

不,只有class_variable_getinstance_variable_getconst_get。没有local_variable_get。但它无论如何都没有意义:局部变量是当前方法体,模块体,类体或脚本体的局部变量,这就是为什么它们称为“局部”变量,毕竟!您只是无法从其他方法访问它们。

  

有办法吗?

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

不,没有办法做到这一点。并且不可能是一种方法。 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'

同样,这个不可能工作,因为局部变量的整点无法从其他任何地方访问

但是有一个非常简单的变体可以完全满足您的需求:

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时,它都会覆盖 {em}仅 副本User::Validations.uUser::Validations.eUser::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

答案 1 :(得分:3)

也许我错过了什么,但你为什么不能test来获得它的价值呢?如果要定义实例变量,则为instance_variable_get

编辑:正如我在下面的评论中所提到的,你可以做到

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

但这对我来说似乎有些疯狂,如果不是很疯狂的话。

答案 2 :(得分:2)

试试这个:

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

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

答案 3 :(得分:1)

Binding可以访问您的局部变量,因此您可以使用绑定#eval

来执行此操作
eval("#{var_name}")

会做到的。显然不要使用不受信任的输入。如果要避免未定义变量的错误,可以使用:

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

E.g。

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

因此,在将来的版本中,您可能会直接使用这些调用。