nop*_*ole 2 ruby debugging ruby-on-rails instance-variables
给定Ruby中的任何对象(在Rails上),我如何编写一个方法,以便它显示该对象的实例变量名称及其值,如下所示:
@x: 1
@y: 2
@link_to_point: #<Point:0x10031b298 @y=20, @x=38>
Run Code Online (Sandbox Code Playgroud)
(更新: inspect除了大对象之外,很难从200行输出中分解变量,比如在Rails中,当你request.inspect或self.inspect在ActionView对象中时)
我还希望能够打印<br>到每个实例变量值的末尾,以便在网页上很好地打印出来.
现在的困难似乎是并非每个实例变量都有一个访问器,因此无法使用obj.send(var_name)调用它
(var_name删除了"@",因此"@x"变为"x")
更新:我想使用递归,它可以打印出更高级的版本:
#<Point:0x10031b462>
@x: 1
@y: 2
@link_to_point: #<Point:0x10031b298>
@x=38
@y=20
Run Code Online (Sandbox Code Playgroud)
我可能会这样写:
class Object
def all_variables(root=true)
vars = {}
self.instance_variables.each do |var|
ivar = self.instance_variable_get(var)
vars[var] = [ivar, ivar.all_variables(false)]
end
root ? [self, vars] : vars
end
end
def string_variables(vars, lb="\n", indent="\t", current_indent="")
out = "#{vars[0].inspect}#{lb}"
current_indent += indent
out += vars[1].map do |var, ivar|
ivstr = string_variables(ivar, lb, indent, current_indent)
"#{current_indent}#{var}: #{ivstr}"
end.join
return out
end
def inspect_variables(obj, lb="\n", indent="\t", current_indent="")
string_variables(obj.all_variables, lb, indent, current_indent)
end
Run Code Online (Sandbox Code Playgroud)
该Object#all_variables方法生成一个包含(0)给定对象的数组和(1)哈希映射实例变量名称到包含(0)实例变量和(1)哈希映射...的数组.因此,它为您提供了一个很好的递归结构.该string_variables函数很好地打印出该哈希值; inspect_variables只是一个方便的包装.因此,print inspect_variables(foo)为您提供换行符分隔选项,并print inspect_variables(foo, "<br />\n")为您提供包含HTML换行符的版本.如果要指定缩进,也可以这样做:print inspect_variables(foo, "\n", "|---")生成(无用的)伪树格式而不是基于制表符的缩进.
应该有一种合理的方法来编写一个 编辑1:我想到了什么.each_variable函数,您可以向其提供回调(不必分配中间存储); 如果我想到的话,我会编辑这个答案以包含它.
这是另一种写它的方式,我觉得它更好一些:
class Object
def each_variable(name=nil, depth=0, parent=nil, &block)
yield name, self, depth, parent
self.instance_variables.each do |var|
self.instance_variable_get(var).each_variable(var, depth+1, self, &block)
end
end
end
def inspect_variables(obj, nl="\n", indent="\t", sep=': ')
out = ''
obj.each_variable do |name, var, depth, _parent|
out += [indent*depth, name, name ? sep : '', var.inspect, nl].join
end
return out
end
Run Code Online (Sandbox Code Playgroud)
该Object#each_variable方法采用了许多可选参数,这些参数不是由用户指定的; 相反,它们被递归用于维持状态.给定的块传递(a)实例变量的名称,或者nil如果变量是递归的根; (b)变数; (c)递归下降的深度; (d),当前变量的父级,或者nil如果所述变量是递归的根.递归是深度优先的.该inspect_variables函数使用它来构建字符串.的obj参数是要遍历所述对象; nl是行分隔符; indent是在每个级别应用的缩进; 并sep分隔名称和值.
编辑2:这并没有真正为你的问题的答案添加任何东西,但是:只是为了证明我们在重新实现中没有丢失任何东西,这里是重新实现all_variables的each_variables.
def all_variables(obj)
cur_depth = 0
root = [obj, {}]
tree = root
parents = []
prev = root
obj.each_variable do |name, var, depth, _parent|
next unless name
case depth <=> cur_depth
when -1 # We've gone back up
tree = parents.pop(cur_depth - depth)[0]
when +1 # We've gone down
parents << tree
tree = prev
else # We're at the same level
# Do nothing
end
cur_depth = depth
prev = tree[1][name] = [var, {}]
end
return root
end
Run Code Online (Sandbox Code Playgroud)
我觉得它应该更短,但这可能是不可能的; 因为我们现在没有递归,所以我们必须明确地维护堆栈(in parents).但这是可能的,所以这种each_variable方法同样适用(我觉得它更好一些).
我明白了... Antal必须在这里给出高级版本......
短版本可能是:
def p_each(obj)
obj.instance_variables.each do |v|
puts "#{v}: #{obj.instance_variable_get(v)}\n"
end
nil
end
Run Code Online (Sandbox Code Playgroud)
或者将其作为字符串返回:
def sp_each(obj)
s = ""
obj.instance_variables.each do |v|
s += "#{v}: #{obj.instance_variable_get(v)}\n"
end
s
end
Run Code Online (Sandbox Code Playgroud)
或更短:
def sp_each(obj)
obj.instance_variables.map {|v| "#{v}: #{obj.instance_variable_get(v)}\n"}.join
end
Run Code Online (Sandbox Code Playgroud)