2010-06-16 35 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 
相關問題