36

What is the best way to write a function (or something DSLish) that will allow me to write this code in Ruby. How would I construct the function write_pair?

username = "tyndall"
write_pair username
# where write_pair username outputs 
username: tyndall

Is it possible to do? Looking for the most simple way to do this.

1
  • Exemplor can not only do this, but even print out list.first: 1. Commented Apr 9, 2010 at 11:06

8 Answers 8

25

Sure it is possible!

My solution tests the var by Object#object_id identity: http://codepad.org/V7TXRxmL
It's crippled in the binding passing style ...
Although it works just for local vars yet, it can be easily be made "universal" adding use of the other scope-variable-listing methods like instance_variables etc.

# the function must be defined in such a place 
# ... so as to "catch" the binding of the vars ... cheesy
# otherwise we're kinda stuck with the extra param on the caller
@_binding = binding
def write_pair(p, b = @_binding)
  eval("
    local_variables.each do |v| 
      if eval(v.to_s + \".object_id\") == " + p.object_id.to_s + "
        puts v.to_s + ': ' + \"" + p.to_s + "\"
      end
    end
  " , b)
end

# if the binding is an issue just do here:
# write_pair = lambda { |p| write_pair(p, binding) }

# just some test vars to make sure it works
username1 = "tyndall"
username  = "tyndall"
username3 = "tyndall"

# the result:
write_pair(username)
# username: tyndall
Sign up to request clarification or add additional context in comments.

4 Comments

where can I read more on this binding (in the way that you are using the term)? this is where I get lost.
I would be seriously tempted to injure anyone who would actually use this code in a project.
This is purely an experiment of boundaries.
18

If it's possible for you to use a symbol instead of the variable name, you could do something like this:

def wp (s, &b)
  puts "#{s} = #{eval(s.to_s, b.binding)}"
end

In use:

irb(main):001:0> def wp (s, &b)
irb(main):002:1>   puts "#{s} = #{eval(s.to_s, b.binding)}"
irb(main):003:1> end
=> nil
irb(main):004:0> var = 3
=> 3
irb(main):005:0> wp(:var) {}
var = 3

Note that you must pass the empty block {} to the method or it cannot get the binding to evaluate the symbol.

2 Comments

If you are going to call .to_s, why not pass a string directly? This way you can even use more complex expressions. Besides, why do you use b.send(:binding) instead of the more obvious b.binding? (I'm new to Ruby). Anyway, it works great and is far simpler than that other solution (the one called write_pair), which I can't even understand
Yes, you can pass a string instead of the symbol (no modifications required), the first line in the solution just refers to the fact that you can't type something wp(var) (like in the question). Don't know/remember why I picked the symbol instead of string, or why I put in .send(:binding) instead of .binding — probably due to thinking of some other solution first and evolving from that. =)
6

I made a vim macro for this:

" Inspect the variable on the current line (in Ruby)
autocmd FileType ruby nmap ,i ^"oy$Iputs "<esc>A: #{(<esc>"opA).inspect}"<esc>

Put the variable you'd like to inspect on a line by itself, then type ,i (comma then i) in normal mode. It turns this:

foo

into this:

puts "foo: #{(foo).inspect}"

This is nice because it doesn't have any external dependencies (e.g. you don't have to have a library loaded up to use it).

Comments

5

You can't actually get a variable's name in Ruby. But you could do something like this:

data = {"username" => "tyndall"}

Or even,

username = "tyndall"
data = {"username", "password", "favorite_color"}
data.each { |param|
   value = eval(param)
   puts "#{param}: #{value}"
}

Comments

4

Building on previous answers relating to symbols & bindings ... if passing in the variable name as a symbol works for you (who doesn't love cutting out extra keystrokes?!), try this:

def wp(var_name_as_sym)
  # gets caller binding, which contains caller's execution environment
  parent_binding = RubyVM::DebugInspector.open{|i| i.frame_binding(2) }
  # now puts the symbol as string + the symbol executed as a variable in the caller's binding
  puts %Q~#{var_name_as_sym.to_s} = #{eval("#{var_name_as_sym.to_s}.inspect", parent_binding)}~
end

aa=1
bb='some bb string'
os = OpenStruct.new(z:26, y:25)

Console output:

> wp :aa
aa = 1
=> nil
> wp :bb
bb = "some bb string"
=> nil
> wp :os
os = #<OpenStruct z=26, y=25>
=> nil

Using ruby 2.2.2p95

(Credit to banister for getting binding of calling context)

Comments

4

This is a simple solution:

  def write_pair(variable)
    puts variable + eval(variable)
  end

This is more readable:

 def write_pair(variable)
    puts 'A' * 100
    puts variable + ': ' + eval(variable).inspect
    puts 'Z' * 100
 end

Invocation:

write_pair "variable"

Comments

3
def write_pair var, binding
  puts "#{ var } = #{ eval(var, binding)}"
end


username = "tyndall"
write_pair "username", binding

This seems weird because binding is never defined, but it works. From Ruby: getting variable name:

The binding() method gives a Binding object which remembers the context at the point the method was called. You then pass a binding into eval(), and it evaluates the variable in that context.

Be sure to pass a string, not the variable.

Comments

0
# make use of dynamic scoping via methods and instance vars
@_binding = binding
def eval_debug(expr, binding = @_binding)
   "#{expr} => #{eval(expr, binding)}"
end

# sample invocation:
x = 10
puts eval_debug "x"
puts eval_debug "x**x"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.