Ruby Structures - Intermediate
We've seen that Ruby treats primitive data structures such as arrays and hashes as objects. We know that we can easily store collections in these data structures and perform whatever functions we need. When dealing with more complex data associations, Ruby classes come to our rescue, allowing us to store attributes, methods, and so on.
There are times when we have a data structure that doesn't quite fit in Ruby's smart collections but the overhead of creating a class seems unnecessary. The question then becomes, "is there a way to store a bunch of attributes for an object without creating an entire class?". Alas, Ruby provides yet another creative solution. The Struct! A struct is essentially a simple class that allows you to encapsulate attributes and accessor methods without having to explicitly define a class. Examine the following IRB session:
:manufacturer, :os) Computer < Struct laptop = Computer.new('Toshiba', 'Windows') #<Computer:0x7fa7039127e0 manufacturer = "Toshiba", os = "Windows" # the struct will have accessor methods for the symbols passed in # attributes are accessible via dot notation or square brackets laptop.manufacturer "Toshiba" laptop[:os] "Windows" # we can set attributes in the same way laptop.manufacturer = "Apple" "Apple" laptop[:os] = "OS X" "OS X" laptop #<Computer:0x7fa7039127e0 manufacturer = "Apple", os = "OS X" # we can return an array of values by calling the values method laptop.values [  "Apple",  "OS X" ]Computer = Struct.new(
One of the benefits of using a struct is it gives meaning to our data. Our laptop object has a "manufacturer" attribute and "os" attribute. If we try to pass in an attribute that we haven't defined we will get an error:
"Silver" NoMethodError: undefined method `color=' for #<struct Computer manufacturer="Apple", os="OS X">laptop.color =
What if we did want to add attributes to our a laptop, such as color or age? Our "Computer" struct is closed, meaning the only attributes of "Computer" are the attributes specified during its creation. What we need is an open struct. Lucky for us, Ruby has the 'ostruct' library, which we can easily mix in.
# we must require the ostruct library to make use of OpenStruct require 'ostruct' true computer = OpenStruct.new #<OpenStruct> computer.manufacturer = "Dell" "Dell" computer.os = "Windows" "Windows" computer.age = "18 months" "18 months"
Ruby's documentation defines OpenStruct as, "a data structure, similar to a Hash, that allows for the definition of arbitary attributes with their accompanying values". The OpenStruct object provides an simple interface for working with arbitrary attributes. Unfortunately, it does not include the additional functionality of a hash, for example it lacks some of the more useful methods such as
include? and so on. Although it employs a hash internally to store the methods and values, it carries additional overhead for setting properties that can effect the performance of an application.
In this chapter, we explored some alternative data structures offered by Ruby, specifically struct and 'ostruct'. These classes provide a convenient way to create data structures with the behavior of a class. The Struct bundles attributes together with accessor methods, without having to write an explicit class. The struct class then generates new subclasses that hold a set of members and their values. We also explored the flexibility of the OpenStruct, which provides a simple interface for adding attributes. Remember, the 'ostruct' library is not a part of Ruby core and therefore must be required in order to make use of OpenStruct.