Your first alternative option for key/value storage is the Struct. The Struct class is like a struct in C or C++. It has a set of variables you can assign to, and nothing else. In Ruby, it's just a shortcut to creating an empty class and adding some attr_accessor statements and a contructor to assign to the attributes.
#!/usr/bin/env ruby # Define the struct Item = Struct.new( :name, :id, :price ) # Assign from constructor i = Item.new( "Watermelons", 1337, "$5.40" ) puts i.name puts i.id puts i.price # Or assign after constructor i.name = "Blue cheese" # This code is equivalent, but more verbose class AnotherItem attr_accessor :name, :id, :price def initialize( *args ) @name, @id, @price = *args end end p = AnotherItem.new( "Sardines", 31337, "$0.99" ) puts p.name puts p.id puts p.price
As you can see, a Struct can replace a hash and store objects in a key/value scheme. This should also be faster since no hash values have to be calculated. Instead of calculating a hash value and doing a lookup in the hash for the key, a simple method lookup will be performed. However, this doesn't let you define new attributes on the fly. This is where OpenStruct comes in.
An OpenStruct is like a Struct, except you don't have to define a list of attributes on construction. Instead, the first time you try to assign to an attribute that doesn't yet exist, the OpenStruct class will create it for you. This gives you all the benefits of hashes without the limitations of a Struct.
#!/usr/bin/env ruby require 'ostruct' i = OpenStruct.new i.name = "Watermelons" i.id = 1337 i.price = "$5.40" puts i.name puts i.id puts i.price
There is, however, one huge caveat when using these classes. The attributes in these examples were chosen very carefully. If you run the second example, you'll see a warning about using the object_id method instead of id, and the id returned is wrong. What is this all about?
Well, these are normal objects, after all. You still have to worry about the methods it already inherited from the Object class. In the first example, everything appears to have worked. However, since Struct creates new methods for each attribute, it's overwritten Object#id on the object.
The second example is a little more tricky. When you try to use any assignment to an attribute, it first checks to see if that method exists. Since Object#id exists, it assumes is can assign to it and tries to. This doesn't work at all, and the value you get when you read the id attribute is incorrect. So always watch your attribute names carefully! Refer to the Object documentation for a list of attribute names you cannot use.