2010-06-16 32 views
2

如果给定了Ruby(on Rails)中的任何对象,我该如何编写一个方法来显示该对象的实例变量名称及其值,像这样:在Ruby中,如何编写一个方法来显示任何对象的实例变量名称和值

@x: 1 
@y: 2 
@link_to_point: #<Point:0x10031b298 @y=20, @x=38> 

更新:inspect会做,除了大的物体也难以从200线输出的分解变量,就像Rails的,当的ActionView你request.inspectself.inspect对象)

我也想成为能够将<br>打印到每个实例变量值的末尾,以便在网页上很好地打印出来。

现在的困难似乎是,不是每一个实例变量有存取,因此它不能与obj.send被称为(VAR_NAME)

(该VAR_NAME有“@”去掉,所以“@ X”变成 “X”)

更新:我想用递归,它可以打印出更先进的版本:

#<Point:0x10031b462> 
    @x: 1 
    @y: 2 
    @link_to_point: #<Point:0x10031b298> 
     @x=38 
     @y=20 
+0

你可以使用'inspect'方法如果这是你的问题,请这样做。也许这就足够了。 – hurikhan77 2010-06-17 00:38:17

回答

5

,我看到了安塔尔必须在这里给高级版本...

短版则可能是:

def p_each(obj) 
    obj.instance_variables.each do |v| 
    puts "#{v}: #{obj.instance_variable_get(v)}\n" 
    end 
    nil 
end 

或将其作为字符串返回:

def sp_each(obj) 
    s = "" 
    obj.instance_variables.each do |v| 
    s += "#{v}: #{obj.instance_variable_get(v)}\n" 
    end 
    s 
end 

或更短:

def sp_each(obj) 
    obj.instance_variables.map {|v| "#{v}: #{obj.instance_variable_get(v)}\n"}.join 
end 
+1

这是最简单的形式,是的。我的解决方案还(a)递归,所以你得到了树结构,并且(b)允许你添加'
'而不用重写你的其他代码。 – 2010-06-17 17:22:18

0

喜欢这个?

# Get the instance variables of an object 
d = Date.new 
d.instance_variables.each{|i| puts i + "<br />"} 

Ruby Documentation on instance_variables

这个概念通常被称为“自省”,(看自己)。

+0

这只打印出变量名称,而不是值 – 2010-06-16 23:29:21

+0

'eval(i)'应该做其余的 – 2010-06-17 02:06:53

+2

@Justin,或避免'eval'并使用'instance_variable_get' – 2010-06-17 03:20:08

6

我可能会写这样的:

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 

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", "|---")产生一个(无用的)人造树格式,而不是基于制表符的缩进。

应该有一个明智的方法来编写一个each_variable函数,您可以提供一个回调函数(不需要分配中间存储);如果我想到某件事情,我会编辑这个答案以包含它。编辑1:我想到了一些东西。

这里是另一种方式来写它,我认为这是稍微更好:

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 

Object#each_variable方法需要一些可选的参数,它没有被设计成由用户指定;相反,它们被递归用来维护状态。给定的块传递(a)实例变量的名称,或者如果变量是递归的根,则传递nil; (b)变量; (c)递归下降的深度;和(d)是当前变量的父亲,或者如果所述变量是递归的根,则为nil。递归是深度优先的。 inspect_variables函数使用它来建立一个字符串。 obj参数是要迭代的对象; nl是行分隔符; indent是在每个级别应用的缩进;和sep分隔名称和值。

编辑2:这并没有真正的答案添加任何你的问题,而是:只是为了证明我们并没有失去在重新实现什么,这里有一个all_variableseach_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 

我觉得它应该更短,但这可能是不可能的;因为我们现在没有递归,所以我们必须明确地维护栈(在parents中)。但这是可能的,所以each_variable方法也可以工作(我认为它有点更好)。

1

这是一个简单的JSON emitter I wrote for another question很快适应:

class Object 
    def inspect!(indent=0) 
    return inspect if instance_variables.empty? 
    "#<#{self.class}:0x#{object_id.to_s(16)}\n#{' ' * indent+=1}#{ 
     instance_variables.map {|var| 
     "#{var}: #{instance_variable_get(var).inspect!(indent)}" 
     }.join("\n#{' ' * indent}") 
    }\n#{' ' * indent-=1}>" 
    end 
end 

class Array 
    def inspect!(indent=0) 
    return '[]' if empty? 
    "[\n#{' ' * indent+=1}#{ 
     map {|el| el.inspect!(indent) }.join(",\n#{' ' * indent}") 
    }\n#{' ' * indent-=1}]" 
    end 
end 

class Hash 
    def inspect!(indent=0) 
    return '{}' if empty? 
    "{\n#{' ' * indent+=1}#{ 
     map {|k, v| 
     "#{k.inspect!(indent)} => #{v.inspect!(indent)}" 
     }.join(",\n#{' ' * indent}") 
    }\n#{' ' * indent-=1}}" 
    end 
end 

这是所有的魔法,真的。现在,我们只需要对某些类型的地方全在检查并没有真正意义(nilfalsetrue,数字等),一些简单的默认值:

module InspectBang 
    def inspect!(indent=0) 
    inspect 
    end 
end 

[Numeric, Symbol, NilClass, TrueClass, FalseClass, String].each do |klass| 
    klass.send :include, InspectBang 
end 
相关问题