I’ve been using Struct.new as a superclass for a while, but today I hit a bit of a gotchya that is worth being aware of.
1.9.3p194 :001 > class Person < Struct.new(:first, :last); end
=> nil
1.9.3p194 :002 > Array(Person.new('Joe', 'Bloggs'))
=> ["Joe", "Bloggs"]
Struct actually implements to_a, returning the list of values as an array. So if you pass instances of your subclass to Array() you’ll get the list of values, not the instance wrapped in an Array, which is probably what you expected.
There are some really annoying problems with OpenStruct that I found while rewriting it. For instance:
OpenStruct behaves similarly to a Hash.
OpenStruct can be built from, updated with, and dump a Hash.
It even has a similar method to Hash#delete(), the OpenStruct#delete_field() method.
Yet the OpenStruct is missing a few key methods like merge or merge!.
These methods would work exactly like the (already defined) OpenStruct#marshal_load().
I believe that OpenStruct would benefit from aliases that make it behave like Hash and abide by the Principle Of Least Surprise.
All of the methods on the OpenStruct are open to easy overwriting.
In addition some of the methods use other methods that are likely to be overwritten like the call to OpenStruct#object_id in the OpenStruct#inspect method.
It also causes problems like this: https://gist.github.com/3460906
Using the OpenStruct#delete_field method wouldn’t actually remove the method given, only the table key/value.
This creates a confusing mismatch in the object and the table.
When using OpenStruct#marshal_load it would correctly create getter and setter methods for each key/value.
It would also delete the entire table.
One of the tests freezes an OpenStruct object and then redefines OpenStruct#frozen? and it works.
Freezing should stop changing the object, in any way.
If you attempted to use the OpenStruct#[]= or OpenStruct#[] method on an OpenStruct it would return a NoMethodError exception for some crazy reason.
In the same vein if you freeze an OpenStruct instance and then attempt to normally create a new accessor it’s supposed to return TypeError. This is specifically tested.
Nice. You can also give Struct.new a block and define instance methods:
Thanks! Super neat. :)
I’ve been using Struct.new as a superclass for a while, but today I hit a bit of a gotchya that is worth being aware of.
Struct actually implements to_a, returning the list of values as an array. So if you pass instances of your subclass to Array() you’ll get the list of values, not the instance wrapped in an Array, which is probably what you expected.
There are some really annoying problems with OpenStruct that I found while rewriting it. For instance:
OpenStructbehaves similarly to aHash.OpenStructcan be built from, updated with, and dump aHash. It even has a similar method toHash#delete(), theOpenStruct#delete_field()method. Yet theOpenStructis missing a few key methods likemergeormerge!. These methods would work exactly like the (already defined)OpenStruct#marshal_load(). I believe thatOpenStructwould benefit from aliases that make it behave likeHashand abide by the Principle Of Least Surprise.OpenStructare open to easy overwriting. In addition some of the methods use other methods that are likely to be overwritten like the call toOpenStruct#object_idin theOpenStruct#inspectmethod. It also causes problems like this: https://gist.github.com/3460906OpenStruct#delete_fieldmethod wouldn’t actually remove the method given, only the table key/value. This creates a confusing mismatch in the object and the table.OpenStruct#marshal_loadit would correctly create getter and setter methods for each key/value. It would also delete the entire table.OpenStructobject and then redefinesOpenStruct#frozen?and it works. Freezing should stop changing the object, in any way.OpenStruct#[]=orOpenStruct#[]method on an OpenStruct it would return aNoMethodErrorexception for some crazy reason. In the same vein if you freeze anOpenStructinstance and then attempt to normally create a new accessor it’s supposed to returnTypeError. This is specifically tested.I am oddly very excited about this – especially around the clarity for tests. That birthday example was beautiful.
I am very excited about this – especially around the clarity for tests. That birthday example was beautiful.