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

Điều này là tốt. Gọi p với đối tượng làm đối số in đầu ra của phương thức inspect. Nhưng, không may, nếu đối tượng có một phương pháp to_s ghi đè sau đó nó sẽ ra đầu ra của rằng:Thi đua đối tượng mặc định # kiểm tra đầu ra?

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

Vì vậy, để khắc phục điều này, chúng ta phải xác định một phương pháp inspect trên đối tượng của chúng tôi:

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

Làm cách nào để tạo phương thức của phương thức inspect đối tượng của tôi theo cách Ruby mặc định như trong dòng 3 của ví dụ mã đầu tiên của tôi?

Gần nhất tôi có là dưới đây, nhưng tôi không chắc chắn nếu nó hoàn toàn đúng

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

Bạn đang sử dụng phiên bản Ruby nào? –

Trả lời

5

Phương pháp mặc định inspect hóa ra là phức tạp đáng ngạc nhiên vì nó cần xử lý đúng các cuộc gọi đệ quy cho chính nó. Dưới đây là triển khai dựa trên mã nguồn Rubinius bỏ qua sự hiện diện của to_s.

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 

Để sử dụng mô-đun này, bạn muốn làm điều gì đó như thế này:

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"> 

Sửa: Vâng, có vẻ như tôi đã đưa ra cơ bản những gì bạn đã làm . Của bạn và của tôi cả hai kết quả trong các đại diện object_id khác nhau hơn so với bản gốc, mặc dù.

Hãy để tôi điều tra nếu có bất kỳ cách nào ràng buộc với việc triển khai chính thức và sử dụng điều đó.

+0

"object_id" thực sự là '(void *)' đối tượng trong MRI, và một tham chiếu con trỏ tương tự trong jruby. Nó là không thể tiếp cận trong mã ruby ​​đồng bằng. Này là số lẻ. –

7

Để làm cho những con số phù hợp với việc thực hiện ban đầu, bạn chỉ cần để lại chuyển các object_id bởi một chút đó là như được hiển thị như sau:

(object_id << 1).to_s(16) 

Phải có thêm một bit được sử dụng cho cờ.

Các vấn đề liên quan