tags:

views:

44

answers:

3

So I know you can say Kernel.const_get("ClassName") and you'll get back the class to which the string corresponds in name. But what about for variables? Is there a way to do:

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

Thanks so much

The fact is that I need it in more complex code, real example:

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

So you see, there's no better way, is there?

+2  A: 

Maybe I'm missing something, but why can't you just do test to get its value? If you are defining instance variables, there is instance_variable_get.

Edit: as mentioned in my comment below, you could do

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

But this seems a little insane to me, if not a lot insane.

theIV
because in reality i have something more complex, e.g.`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"`
aharon
If you REALLY need it to be a method of some sort, it sounds like you need `eval`—http://ruby-doc.org/core/classes/Kernel.html#M005922—though, I don't think I've ever used it. Couldn't you make this be something more like a hash?
theIV
eval was the way I was doing it already, trying to find something better (for exceptions and stuff) but haven't. thanks though. (see my original post edited for why I need it?)
aharon
+3  A: 

No, there is only class_variable_get, instance_variable_get and const_get. There is no local_variable_get. But it wouldn't make sense anyway: local variables are local to the current method body, module body, class body or script body, that's why they are called "local" variables, after all! You simply cannot access them from another method.

Is there a way to do:

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

No, there is no way to do this. And there cannot possibly be a way to do it. test is a local variable, which means it only exists in the current script body. It does not exist inside the body of some_method_here. That's the whole point of local variables: you cannot ever, under any circumstances, access them from somewhere else.

Regarding your comment on another answer:

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'

Again, this cannot possibly work, since the whole point of local variables is that they cannot be accessed from anywhere else.

But there is a pretty simple variation which does exactly what you want:

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'

This can of course be further simplified to:

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

Given that you can only pass in a limited number of different options, it's probably better to use symbols instead:

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

What you are asking is basically: pass in a key, get a value out. That's exactly what a Hash is.

EDIT: After the revised question, this is the best that I could come up with:

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

However, I think there is something seriously wrong with the design. You overwrite singleton methods of User::Validations based on different instances of User. By the very definition of singleton method, there can only ever be one copy of those in the system. But you have many different instances of User, and every time you call User#validate it will overwrite the only copies of User::Validations.u, User::Validations.e and User::Validations.p at which point they will start to behave completely differently for the entire system.

In other words, you are changing the behavior of the entire system based on a single instance. And there can be many instances and every time, the behavior of the system changes.

That just can't be right.

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
Jörg W Mittag
+1 I like the hash approach a whole lot more.
theIV
Well, you can always do `eval(str)`...
aharon
@aharon: And *I* can always do `my_func('require "fileutils"; FileUtils.rm_rf("/")')`
Jörg W Mittag
Bingo. Exactly why the hash is the better approach.
theIV
Well, sometimes it's not that simple. See my original question for the actual application.
aharon
You're absolutely right. I didn't realize that. Thanks so much.
aharon
+1  A: 

try this:

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
banister
I may do that, un-dry as it is.
aharon
i think you're taking DRY too far. Even if you COULD do it the code would likely be cryptic. Clear and verbose > DRY and cryptic
banister