2011-04-24 2 views
9
o = Object.new 
o.instance_eval { @str = "foo" } 
p o # => #<Object:0x5dd1a0 @foo="bar"> 

좋은 결과입니다. 객체를 인수로 사용하여 p을 호출하면 inspect 객체의 출력이 인쇄됩니다. 그러나, 불행하게도, 경우 개체가 출력이의 출력이됩니다 다음 재정의 to_s 방법이 있습니다에뮬레이트 된 기본 개체 # inspect Output?

class << o 
    def inspect; "blah"; end 
end 
p o # => "blah" 
:

class << o 
    def to_s; @str; end 
end 
p o.to_s # => "foo" 
p o # => foo 

그래서이 문제를 해결하기를, 우리는 우리의 객체에 대해 inspect 방법을 정의해야

첫 번째 코드 예제의 3 행에 표시된 것처럼 객체의 inspect 메서드가 기본 Ruby 방식으로 출력되도록하려면 어떻게해야합니까?

내가 가진 가장 가까운 아래이지만, 꽤 맞아 있는지 확실하지 않습니다

class << o 
    def inspect 
    vars = instance_variables.collect { |v| v.to_s << "=#{instance_variable_get(v).inspect}"}.join(", ") 
    "#<#{self.class}:0x#{object_id} #{vars}>" 
    end 
end 
+0

사용중인 Ruby 버전은 무엇입니까? –

답변

5

기본값 인 inspect은 자체에 대한 재귀 호출을 적절히 처리해야하기 때문에 놀랍도록 복잡합니다. 다음은 Rubinius 소스 코드를 기반으로 한 구현으로 to_s의 존재를 무시한 것입니다. 방금 같이 하나 비트에 의해 것으로, object_id 왼쪽으로 시프트 할 필요가

class Foo 

    include DefaultInspect 

    def to_s 
    @foo 
    end 
end 

f = Foo.new 
f.instance_eval { @foo = f } 
p f  #=> #<Foo:0x8042ad58 @foo=#<Foo:0x8042ad58 ...>> 
0
irb> o = Object.new.tap{ |o| o.instance_variable_set :@foo, "bar" } 
#=> #<Object:0x00000102849600 @foo="bar"> 

irb> def o.to_s; @foo; end; o 
#=> bar 

irb> module MyInspect 
irb> def inspect 
irb>  vars = instance_variables.map do |n| 
irb>  "#{n}=#{instance_variable_get(n).inspect}" 
irb>  end 
irb>  "#<%s:0x%x %s>" % [self.class,object_id,vars.join(', ')] 
irb> end 
irb> end 

irb> o.extend MyInspect 
#=> #<Object:0x81424b00 @foo="bar"> 

편집 : 나는 당신이 이미 한 기본적으로 무엇을 함께했다 글쎄, 그것은 본다 . 당신과 나의 두 사람 모두 원본과 다른 object_id 표현을합니다.

공식 구현에 바인딩 할 방법이 있는지 조사해보고 사용하십시오.

+0

"object_id"는 실제로 MRI의 객체에 대한'(void *) '이며 jruby의 비슷한 포인터 참조입니다. 일반 루비 코드에서는 액세스 할 수 없습니다. 이것은 이상합니다. –

7

숫자가 원래의 구현을 일치하도록 :

module DefaultInspect 

    Thread.current[:inspected_objects] = {} 

    def inspected_objects 
     Thread.current[:inspected_objects] 
    end 

    def inspect_recursion_guard 
     inspected_objects[object_id] = true 
     begin 
     yield 
     ensure 
     inspected_objects.delete object_id 
     end 
    end 

    def inspect_recursion? 
     inspected_objects[object_id]  
    end 

    def inspect 
     prefix = "#<#{self.class}:0x#{self.__id__.to_s(16)}" 

     # If it's already been inspected, return the ... 
     return "#{prefix} ...>" if inspect_recursion? 

     # Otherwise, gather the ivars and show them. 
     parts = [] 

     inspect_recursion_guard do 
     instance_variables.each do |var| 
      parts << "#{var}=#{instance_variable_get(var).inspect}" 
     end 
     end 

     if parts.empty? 
     str = "#{prefix}>" 
     else 
     str = "#{prefix} #{parts.join(' ')}>" 
     end 

     str.taint if tainted? 

     return str 
    end 

end 

, 당신은 같은 것을 할 것이 모듈을 사용하려면 다음과 같이 표시됩니다 :

(object_id << 1).to_s(16) 

플래그에 여분의 비트가 있어야합니다.