Putting a variable name = value format in Ruby
I would like to add some debugs for m开发者_Go百科y simple ruby functions and I wrote a function as below,
def debug(&block)
varname = block.call.to_s
puts "#{varname} = #{eval(varname,block)}"
end
debug {:x} #prints x = 5
debug {:y} #prints y = 5
I understand that eval is evil. So I have two questions.
- Is there any way to write that debug method without using eval? If NO is there a preferred way to do this?
- Is there any way to pass a list of arguments to this method? I would ideally prefer debug {:x, :y. :anynumOfvariables}. I could not quite figure out how to factor that into the debug method (i.e, to take a list of arguments)
Just use arrays. You can use the Array
method to ensure that you will always have an array, even if someone passes in only a single value:
def debug(&block)
Array(block[]).each do |var| puts "#{var} = #{eval var.to_s, block}" end
end
x, y = 3, 5
debug {:x} # => "x = 3"
debug {[:x, :y]} # => "x = 3" "y = 5"
BTW: passing a block as the binding no longer works in Ruby 1.9. (Despite the fact that the documentation says it does work.) You have to explicitly call Proc#binding
to get a Binding
object for that Proc
:
def debug(&block)
Array(block.()).flatten.each do |var|
puts "#{var} = #{eval var.to_s, block.binding}"
end
end
Fortunately, this already works in Ruby 1.8, so you can futureproof your code by including it.
An alternative would be to forgo the block altogether. I mean, you already force the user of the debug
to use the unfamiliar idiom of passing arguments in the block instead of in parentheses. Why not force them to just pass the binding instead?
def debug(*vars, bnd)
vars.each do |var|
puts "#{var} = #{eval var.to_s, bnd}"
end
end
x, y = 3, 5
debug :x, binding # => "x = 3"
debug :x, :y, binding # => "x = 3" "y = 5"
This has the added flexibility that they can actually pass a different binding than the one at the callsite, e.g. if they want to actually debug a piece of code in a different piece of the application.
BTW: here's some fun with Ruby 1.9.2's parameter introspection (Proc#parameters
):
def debug(&block)
block.parameters.map(&:last).each do |var|
puts "#{var} = #{eval var.to_s, block.binding}"
end
end
x, y = 3, 5
debug {|x|} # => "x = 3"
debug {|x, y|} # => "x = 3" "y = 5"
And how I must use those methods to get names and values of variables from each loop, to place them as keys and values of nested hash?
The *attributes array is passed as an method's parameter. I want to iterate through it, and every variable's name use as a key to nested hash and variable's value as value.
I got this:
def get_varname(&block)
varname = block.call.to_s
return varname
end
def create_instance_hash(env_attrs_obj, *attributes)
if not env_attrs_obj.has_key?("instances")
env_attrs_obj["instances"] = ""
end
attributes.each do |attr|
attr_name = get_varname{:attr}
env_attrs_obj["instances"][attr_name] = attr
end
end
instance_nat_ip_pub = "ip_pub_addr"
instance_nat_ip_prv = "ip_addr"
instance_nat_ami = "AMI_NAME"
instance_nat_aws_ssh_key_id = "AWS_SSH_KEY_ID"
instance_nat_id = "instance_id"
env_hash = {}
create_instance_hash(env_hash, *[instance_nat_ip_pub, instance_nat_ip_prv, instance_nat_ami, instance_nat_aws_ssh_key_id, instance_nat_id])
But:
def attrs_each_test(*attributes)
attributes.each do |attr|
puts get_varname{:attr}
end
end
And it outputs:
attr
attr
attr
attr
attr
The error when I run this create_instance_hash:
Line 12:in `[]=': string not matched (IndexError)
from t.rb:12:in `create_instance_hash'
from t.rb:10:in `each'
from t.rb:10:in `create_instance_hash'
from t.rb:24
How I can correct this error and achieve my goal?
精彩评论