Hidden feature in Ruby's Struct

This article originally appeared upon on texperts.com

Ruby LogoThe Ruby core library contains a nice little utility class called Struct, which provides a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class. So this:

class Customer
  attr_accessor :name
  attr_accessor :address

  def initialize(name, address)
    @name = name
    @address = address
  end
end

is (broadly) equivalent to this:

Customer = Struct.new(:name, :address)

Much nicer (and DRYer)!

But what if you want to define methods on the new class that you’ve just created? The Pickaxe Book says:

Ruby 1.9 and later allow you to pass a block to a Struct’s constructor. This block is evaluated in the context of the new struct’s class and hence allows you conveniently to add instance methods to the new struct.

Unfortunately most of us haven’t (yet) moved to Ruby 1.9, but there is good news! It turns out that this functionality has actually been present ever since 1.8.3 (here‘s the relevant ChangeLog entry, although note that when it refers to [ruby-talk:02606], it should really refer to [ruby-core:02606]).

Imagine, for example, that we wanted to add a custom to_s method to our Customer class:

Customer = Struct.new(:name, :address) do
  def to_s
    "A customer called '#{name}' living at '#{address}'"
  end
end

Sweet!

Update (2007-09-05)

It turns out that there is another commonly used idiom which achieves much the same effect:

class Customer < Struct.new(:name, :address)
  def to_s
    "A customer called '#{name}' living at '#{address}'"
  end
end

However, it also turns out that both of these idioms may have problems when combined with Rails. For further information see this discussion on the ruby-talk mailing list.

2 Responses to “Hidden feature in Ruby's Struct”


  1. 1 John Prince May 12, 2009 at 7:01 pm

    Very helpful! I’ve used a sort of class access like the last one shown:

    Customer = Struct.new(:name, :address)

    class Customer
    def to_s

    end
    end

    But both of your examples are cleaner.

  2. 2 Tilo May 17, 2012 at 12:35 pm

    or with metaprogramming:

    Customer = Struct.new(:name, :address)
    Customer.class_eval do
    def to_s
    “A customer called ‘#{name}’ living at ‘#{address}'”
    end
    end


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s





%d bloggers like this: