1

Given an object:

object_name = "hello"

is there any way to get the variable's name "object_name"? I need to create a string with the variable's name and the value in it, e.g.

"The name of the variable is 'object_name' and its value is 'hello'"

I tried:

object_name.object_id
# => 20084556
sawa
  • 165,429
  • 45
  • 277
  • 381
Peter
  • 69
  • 4

4 Answers4

3
def find_var(in_binding, object_id)
  name = in_binding.local_variables.find do |name|
    in_binding.local_variable_get(name).object_id == object_id
  end

  [name, in_binding.local_variable_get(name)]
end

the_answer = 42
find_var binding, the_answer.object_id # => [:the_answer, 42]

foo = 'bar'
baz = [foo]
find_var binding, baz.first.object_id # => [:foo, "bar"]

Obvious downfall - two variables pointing to the same object. Aka

a = b = 42
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • Instead of using `object_id`, you could compare the objects via `equal?` – Stefan Apr 12 '17 at 13:07
  • @Stefan, you can. OP wanted `object_id` for some reason in the question. I'm not sure what possible use case this can have anyway. – ndnenkov Apr 12 '17 at 13:09
  • I see. I thought this was the OP's attempt to get the variable's name. – Stefan Apr 12 '17 at 13:12
  • Concerning the downfall: call the method `find_vars`, replace `find` with `select` and let it return an array. If you want just the first value then you can call `find_vars()[0]`. – mdesantis Apr 12 '17 at 13:23
  • @mdesantis, yes, but I expect that if I call `whatever binding, b.object_id`, where `a = b = 42`, I should get `b` back, not `a` or `[a, b]`. – ndnenkov Apr 12 '17 at 13:26
  • Well no, I do not, why a value should be bound just to one single variable name? – mdesantis Apr 12 '17 at 13:28
  • @mdesantis, well it's not but that is what OP is asking for the way I understand it. – ndnenkov Apr 12 '17 at 13:39
  • Yes actually it is a refactoring: it's better to write methods that conceptually do the things they should do and adapt your code to it, instead of writing a method that do exactly what you want, but has a conceptual flaw. in this case, in your "backend" code the method should be pluralized, since the bound variables can be more then one. Then in your "frontend" code you use just the first var name. If you write the code in this way, you can omit the downfall part, think about it. It's just a refactoring matter anyway. – mdesantis Apr 12 '17 at 16:56
1

The only way I can come up with is something along these lines:

binding.local_variables.each do |var|
  puts "The name of the variable is '#{var}' and its value is '#{eval(var.to_s)}'"
  # or
  # puts "The name of the variable is '#{var}' and its value is '#{binding.local_variable_get(var)}'"
end
# The name of the variable is 'object_name' and its value is 'hello'

Of course this will output all local variables that are currently in scope and, well, eval.

Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
  • 2
    you can use `binding.local_variable_get("variable")` instead of `eval` http://stackoverflow.com/a/30840502/584552 – mdesantis Apr 12 '17 at 13:14
1

Just out of curiosity:

object_name = "hello"
var_name = File.readlines(__FILE__)[__LINE__ - 2][/\w+(?=\s*=)/]
puts "Variable named '#{var_name}' has value '#{eval(var_name)}'"

> ruby /tmp/d.rb
#⇒ Variable named 'object_name' has value 'hello'
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
0

Took me a while, but I solved this eventually. Thanks to all for the above answers. Here are the feature file, step definitions and methods I used to work it out.

Feature: try and work out a simple version of the f2d pages that could be used for all

  Scenario Outline: first go
    Given I set up an object for these things
      | page_1   | page_2   |
      | <page_1> | <page_2> |
    Then I create objects and output two things for one

    Examples:
      | page_1    | page_2      |
      | elephants | scary pants |

Step Definitions:

Given(/^I set up an object for these things$/) do |table|
  set_up_a_hash_from_a_table(table)
end

Then(/^I create objects and output two things for one$/) do
  do_something_with_a_hash
end

Methods:

def set_up_a_hash_from_a_table(table)
  @objects = Hash.new
  @summary = Array.new
  data = table.hashes

  data.each do |field_data|
    @objects['page_1'] = field_data['page_1']
    @objects['page_2'] = field_data['page_2']
  end
end

def do_something_with_a_hash

  show_me_page_1

  show_me_page_2

  puts "Summary is #{@summary}"

end

def show_me_page_1
  do_stuff_for_a_page('page_1')
end

def show_me_page_2
  do_stuff_for_a_page('page_2')
 end

def do_stuff_for_a_page(my_object)
  puts "key is #{my_object} and value is #{@objects[my_object]}"
  @summary.push("#{my_object} - #{@objects[my_object]}")
  end

Result:

#=> key is page_1 and value is elephants

#=> key is page_2 and value is scary pants

#=> Summary is ["page_1 - elephants", "page_2 - scary pants"]
Peter
  • 69
  • 4