andersonvom
andersonvom

Reputation: 11861

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?

Upvotes: 4

Views: 333

Answers (1)

mu is too short
mu is too short

Reputation: 434985

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.

Upvotes: 5

Related Questions