16

Why is_a? returns false for a Hash class?

Example:

value = {"x" => 3, "y" => 2}

puts value.class
puts value.is_a?(Hash)

Output:

Hash
false

Im using Ruby 1.9.2

UPDATED: full source of my class:

class LatLng
  include Mongoid::Fields::Serializable

  attr_reader :lat, :lng

  def serialize(value)
    return if value.nil?

    puts value.class
    puts value.is_a?(Hash)

    if value.is_a?(self.class)
      puts "is geopoint" + value.to_json
      {'lng' => value.lng.to_f, 'lat' => value.lat.to_f}
    elsif value.is_a?(Hash)
      hash = value.with_indifferent_access
      puts "is hash" + value.to_json
      {'lng' => hash['lng'].to_f, 'lat' => hash['lat'].to_f}
    end
  end

  def deserialize(value)
    return if value.nil?

    value.is_a?(self.class) ? value : LatLng.new(value['lat'], value['lng'])
  end

  def initialize(lat, lng)
    @lat, @lng = lat.to_f, lng.to_f
  end

  def [](arg)
    case arg
      when "lat"
        @lat
      when "lng"
        @lng
    end
  end

  def to_a
    [lng, lat]
  end

  def ==(other)
    other.is_a?(self.class) && other.lat == lat && other.lng == lng
  end
end
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Alexey Zakharov
  • 24,694
  • 42
  • 126
  • 197

3 Answers3

23
#irb
ruby-1.9.3-p0 :001 > value = {"x" => 3, "y" => 2}
 => {"x"=>3, "y"=>2} 
ruby-1.9.3-p0 :002 > value.is_a?(Hash)
 => true

try to disable any gems/extensions you have loaded, and try with clean ruby

UPDATE:

try value.is_a?(::Hash)

PS: try to read about Duck Typing in Ruby. Maybe you should call value.respond_to?(:key) instead of value.is_a?(Hash)

zed_0xff
  • 32,417
  • 7
  • 53
  • 72
  • It works in irb, but not in my sample. I've listed whole class source – Alexey Zakharov Dec 21 '11 at 13:48
  • 1
    Just out of curiosity, why does this work? (`::Hash`). Read a post on double colon in Ruby but I don't understand why it would be applicable in this case. – Batkins Dec 21 '11 at 17:39
  • Zed, Thanks. =) Found same solution. – Alexey Zakharov Dec 21 '11 at 18:53
  • By including the module Mongoid::Fields::Serializable means Ruby will look in Mongoid::Fields::Serializable for anything named Hash first, then it will look in Mongoid::Fields for Hash, until it finds Hash. ::Hash means look in the global namespace for Hash. See also [this SO question](http://stackoverflow.com/questions/4855926/in-ruby-how-does-a-class-defined-in-a-module-know-the-modules-constants). – forforf Dec 22 '11 at 22:04
  • Just had the same issue inside of a Resque Job class with the Resque Status plugin. Turns out the plugin adds `Resque::Plugins::Status::Hash` – Jeremy F. Dec 17 '15 at 15:28
5

When I added "::" before Hash class it starts working.

puts value.class
puts value.is_a?(::Hash)

Output:

Hash
true
skayred
  • 10,603
  • 10
  • 52
  • 94
Alexey Zakharov
  • 24,694
  • 42
  • 126
  • 197
2

It doesn't.

dave@hpubuntu1:~ $ rvm list

rvm rubies

   ruby-1.8.7-p334 [ i386 ]
   jruby-1.6.2 [ linux-i386-java ]
   ruby-1.9.2-p0 [ i386 ]
   ruby-1.9.2-p290 [ i386 ]
   ruby-1.9.3-p0 [ i386 ]
=> ruby-1.9.2-p180 [ i386 ]

dave@hpubuntu1:~ $ pry
pry(main)> value = {"x" => 3, "y" => 2}
=> {"x"=>3, "y"=>2}
pry(main)> value.is_a? Hash
=> true

A Mongoid Hash isn't a pure-Ruby Hash, nor does it extend it. You should check the actual type, probably by using type.

Just because something prints out Hash doesn't mean (a) that it's part of the inheritance chain you think it is, and (b) that it's a Hash (witness ActiveRecord Array, which lies, to a degree).

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • It works in irb, but not in my sample. I've listed whole class source – Alexey Zakharov Dec 21 '11 at 13:48
  • 1
    @AlexeyZakharov If it's a Mongoid hash you should probably check explicitly for the full class--a Mongoid Hash isn't a Ruby Hash, nor does it extend it. [docs](http://rubydoc.info/github/mongoid/mongoid/master/Mongoid/Fields/Serializable/Hash) – Dave Newton Dec 21 '11 at 13:52