1. 19

  2. 7

    Nice. You can also give Struct.new a block and define instance methods:

    irb(main):002:0> Struct.new('Foo', :bar) do
    irb(main):003:1*   def baz; :omg; end
    irb(main):004:1> end
    => Struct::Foo
    irb(main):005:0> Struct::Foo.new.baz
    => :omg
    1. 1

      Thanks! Super neat. :)

    2. 2

      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.

      1. 1

        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.
        1. 1

          I am oddly very excited about this – especially around the clarity for tests. That birthday example was beautiful.

          1. 1

            I am very excited about this – especially around the clarity for tests. That birthday example was beautiful.