Home > database >  How to make ruby object attributes hidden from inspect
How to make ruby object attributes hidden from inspect

Time:11-12

So I like to use ruby inspect for debugging, however I have a class that have an array that have 6000 elements and whenever I puts obj.inspect the array clutters the entire screen. Is there any way to make the array attribute hidden from inspect?

class Test
  def initialize
    @x = 1
    @array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  end

  def myinspect
    # ??
  end
end

c = Test.new
puts c.inspect # <Test:0x00007f1d49e33b00 @x=1, @array=[1, 2, 3, 4, 5, 6, 7, 8, 9]>
puts c.myinspect # <Test:0x00007f1d49e33b00 @x=1>

CodePudding user response:

You can redefine any method in Ruby. So, in your case, you may just redefine inspect in your class Test.

Here is an example.

class Test
  alias_method :inspect_original, :inspect if ! self.method_defined?(:inspect_original)  # backup

  def inspect
    "<#{self.class.name}: @x=#{@x.inspect} @array.size=#{@array.size}>"
  end
end

Test.new.inspect
# => "<Test: @x=1 @array.size=9>"

CodePudding user response:

Probably it's possible to use more elegance conversion for object id, but not sure by much

class Test
  def initialize
    @x = 1
    @array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  end

  def myinspect
    "#<#{self.class.name}:0x#{(object_id * 2).to_s(16).rjust(16, '0')} @x=#{instance_variable_get(:@x)}>"
  end
end

And then

test = Test.new

puts test.inspect
#<Test:0x000055e35c5dc4c0 @x=1, @array=[1, 2, 3, 4, 5, 6, 7, 8, 9]>

puts test.myinspect
#<Test:0x000055e35c5dc4c0 @x=1>

Without this conversion your code will be not so ugly

For example rails don't use this object id

Probably you don't need it too

CodePudding user response:

To use my_inspect we need the following regular expression, which I've written in free-spacing mode to make it self-documenting.

R = /
    ,\ @  # match ', @'
    \w    # match one or more word characters
    =\[   # match '=['
    .*?   # match one or more characters lazily
    \]    # match ']'
    /x    # invoke free-spacing regex definition mode

This is conventionally written

R = /, @\w =\[.*?\]/

We then create the class Test to contain one instance variable holding an integer and two instance variables holding arrays, and methods initialize and my_inspect.

class Test
  def initialize
    @x = 1
    @array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    @tom_cat = ['Fluffy', 'Tiger', 'Max']
  end

  def my_inspect
    inspect.gsub(R,'')
  end
end

When inspect is invoked on an instance of Test we want no reference to instance variables holding arrays.

Test.new.inspect
  #=> "#<Test:0x00007f962c102740 @x=1>"

Voilà!


Another approach is to override the built-in inspect. There are two ways to do that. The first is create an alias for the built-in method inspect, as @MasaSakano has done in his/her answer. Another way is to prepend a module containing the new method inspect. I'll describe that option below.

The first step is to create a module with the new method inspect that will be prepended to the class Test.

module M  
  def inspect
    method(__method__).super_method
                      .call
                      .gsub(R,'')
  end
end

See Kernel#__method__ and Method#super_method.


Next create the class Test which prepends module M.

class Test
  def initialize
    @x = 1
    @array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    @tom_cat = ['Fluffy', 'Tiger', 'Max']
  end
  prepend M
end

See Module#prepend.


Try it!

Test.new.inspect
  #=> "#<Test:0x00007f962c852900 @x=1>"

Voilà again!

CodePudding user response:

So I've decided to make an inspect_except

class Test
  def initialize
    @x = 1
    @array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
  end

  def inspect_except(*attrs)
    r = "<#{self.class.name}:#{object_id}"
    instance_variables.each do |var|
      next if attrs.include? var
      r  = " #{var}=#{instance_variable_get var}"
    end
    r  = ">"
  end
end

c = Test.new
puts c.inspect_except(:@array)
  •  Tags:  
  • ruby
  • Related