Pergunta

I have built a subclass of Hash, and whenever I call puts with an instance of my class as an argument, I get the following:

> puts data
TypeError: can't convert Reporting::Search::Data to Array (Reporting::Search::Data#to_ary gives Reporting::Search::Data)

Does anyone have any idea what to do? I'd like it to output a string representation of a Hash, just like calling puts with an ordinary Hash would do.

My class is really simple:

  class Data < HashWithIndifferentAccess
    def method_missing meth, *args, &block
      if meth.to_s =~ /=$/
        send :[]=, meth.slice(0...-1), *args
      elsif args.empty?
        fetch meth, Data.new
      else
        super meth, *args, &block
      end
    end

    def compact!
      delete_if do |k,v|
        v.compact! if v.is_a?(Data)
        v.blank?
      end
    end
  end
Foi útil?

Solução

Looks like the issue is to do with your use of method_missing.

When you call puts, it tries to output the object in a human-readable form, so it calls to_ary on the object. However, you haven't defined to_ary on your class, so it falls back to method_missing where it gets confused and fails.

Define, to_ary as a stub, and I'm not getting the error anymore.

def to_ary
end

This is one of the many pitfalls of metaprogramming in Ruby!

Outras dicas

You have two options, either write a to_s method. Or you could see what the .inspect instance method gives you, though it may not be very useful!

Have you tried adding a to_s method like this?

def to_s
  super.to_s
end

Also, it may not be the only reason you're having issues, but you've named your subclass Data which is already a class in Ruby.

You are violating Ruby's type conversion protocols in various ways.

First off, your object responds to to_int, to_float, to_str, to_ary etc. without actually being an integer, a float, a string or an array. The three letter (ahem f-l-o-a-t ahem) versions of those type conversion methods are supposed to be used only in the case where the object in question really absolutely positively is an integer, float, string or array and is just not represented as an instance of Integer, Float, String or Array.

However, your object isn't an Array, it's a map. So, it should only respond to to_hash, if at all.

And secondly, the return value of to_int, to_float, to_str, to_ary etc. should be an instance of the Integer, Float, String or Array class.

So, you are implementing methods that you shouldn't even be implementing in the first place and you are returning the wrong value from them. Doing that for something as deeply ingrained in the core of Ruby as its type conversion protocols is bound to wreak all sorts of havoc of which you are only seeing the beginning.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top