How to use Struct to simplify your Ruby class definitions

Many times when I'm creating a prototype of an application with Ruby I'll create my classes using a simple Struct technique I learned from a friend. This technique of using the Struct class makes my code shorter, and is at least as readable as writing a class out using any other method.

For example, in the following sample Ruby code, I define a simple Person class using this Struct shortcut:

class Person <

  # a define the fields for my version of a "Person"
  Struct.new(:first_name, :last_name, :address1, :address2,
             :city, :state, :zip)
end

Using this technique to define my Person class, I can then treat my class just like I normally would, using getter and setter methods to work with the fields, as shown in this larger class example:

class Person <

  # a define the fields for my version of a "Person"
  Struct.new(:first_name, :last_name, :address1, :address2,
             :city, :state, :zip)

  # a method to print out a csv record for the current Person.
  def print_csv_record
    # use the ternary operator to use one output format or the other
    last_name.length==0 ? printf(",") : printf("\"%s\",", last_name)
    first_name.length==0 ? printf(",") : printf("\"%s\",", first_name)
    address1.length==0 ? printf(",") : printf("\"%s\",", address1)
    address2.length==0 ? printf(",") : printf("\"%s\",", address2)
    city.length==0 ? printf(",") : printf("\"%s\",", city)
    state.length==0 ? printf(",") : printf("\"%s\",", state)
    # no commas on this last field
    zip.length==0 ? printf("") : printf("\"%s\"", zip)
    printf("\n")
  end
end

As you can see, my print_csv_record method accesses each field that I defined in my Struct.new declaration. Pretty simple, and pretty cool, right?

If you haven't used the Struct class like this before, here's a quick introduction to it from its documentation on the Ruby doc site:

A Struct is a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class. The Struct class is a generator of specific classes, each one of which is defined to hold a set of variables and their accessors.

Accessing your class fields

As you might guess, we can also access our getters and setters from outside our Ruby class. The following code snippet shows how I create an array of Person objects later on in my code, and how I access the class fields I need:

people = Array.new

# loop through each record in the csv file, adding each record to our array
f = File.open(input_file, "r")
f.each_line { |line|
  words = line.split(',')
  p = Person.new
  # do a little work here to get rid of double-quotes and blanks
  p.last_name = words[0].tr_s('"', '').strip
  p.first_name = words[1].tr_s('"', '').strip
  p.address1 = words[2].tr_s('"', '').strip
  p.address2 = words[3].tr_s('"', '').strip
  p.city = words[4].tr_s('"', '').strip
  p.state = words[5].tr_s('"', '').strip
  p.zip = words[6].tr_s('"', '').strip
  people.push(p)
}

# a simple block to sort the data by the last_name field
people.sort! { |a,b| a.last_name.downcase <=> b.last_name.downcase }

# print out all the sorted records (just print to stdout)
people.each { |p|
  p.print_csv_record
}

There are several other ways you can use the Ruby Struct class to simplify your Ruby class definitions, and I encourage you to read the Struct documentation page for further examples.

Other notes

Of course you can also define class fields and then use the attr_reader, attr_writer, or attr_accessor methods to define access to those fields, but for most of my work, I prefer this shorter and simpler approach using the Struct class.