Search code examples
rubynullopenstruct

OpenStruct.new stores attribute but doesn't retrieve it


After creating a new Ruby OpenStruct object, I am able to store attributes but not to retrieve them (I get a blank line and it returns nil instead):

obj = OpenStruct.new # => #<OpenStruct>
obj.x = 10
obj.y = 20
obj                  # => #<OpenStruct x=10, y=20>
obj.x                # => 10
obj.y                #  
                     # => nil

If I try to store other properties with different names, everything works as expected. This problem seems to happen only when I store a property named y. I'm using the following version:

ruby 1.9.2p320 (2012-04-20 revision 35421) [i686-linux]

Does anybody have an idea of what's going on?


Solution

  • Something somewhere is pulling in Psych for YAML stuff. Psych patches Kernel to add a psych_y method which is aliased to y. So, everything has a y method defined:

    > o = OpenStruct.new
    > o.method(:y)
     => #<Method: OpenStruct(Kernel)#psych_y> 
    

    AFAIK, OpenStruct uses method_missing and an internal Hash to produce accessor and mutator methods; but, there's already a y from that "friendly" patch to Kernel so OpenStruct's magic doesn't get to handle the y method because Psych's magic is in the way. The mutator, y=, is fine though so you can safely o.y = 11 and see your 11 inside o.

    You could remove the y like this:

    > o = OpenStruct.new
    > o.class_eval('undef_method :y')
    > o.y = 11
    > o.y
     => 11 
    

    You could probably remove the method from Kernel and hope that nothing depends on that silly y alias:

    > Kernel.send(:undef_method, :y)
    > o = OpenStruct.new
    > o.y = 11
    > o.y
     => 11 
    

    Or you could just remove it from OpenStruct:

    > OpenStruct.send(:undef_method, :y)
    > o = OpenStruct.new
    > o.y = 11
    > o.y
     => 11
    

    This sort of thing is why a lot of people don't like monkey patching, especially monkey patching something as fundamental as Kernel.