Ruby SuperStruct: enhanced version of the standard Ruby Struct

A few months ago I created a custom class to enhance the base Ruby Struct object. What I needed for my Whois library was the ability to create a new instance from a Hash.

Loser = Struct.new(:id, :name)
Winner = SuperStruct.new(:id, :name)

# The default Struct accepts up to N arguments
# where N is the total number of elements defined in the Struct.
Loser.new(:id => "1", :name => "John Doe")
# => #<struct Loser id={:name=>"John Doe", :id=>"1"}, name=nil>

Winner.new(:id => "1", :name => "John Doe")
# => #<struct Winner id="1", name="John Doe">

Because the first argument is mapped to the :id element, the Hash is stored as value for the id attribute.

In a standard Ruby Struct I have to map each Hash value to the specific position in the Struct, loosing the flexibility of passing a Hash with optional keys.

hash = { :name => "John Doe" }

Loser.new(hash)
# => #<struct Loser id={:name=>"John Doe"}, name=nil>
Loser.new(nil, hash[:name])
# => #<struct Loser id=nil, name="John Doe">

Winner.new(hash)
# => #<struct Winner id=nil, name="John Doe">

Also, I'd like to be able to pass a block to the initialize method and yield on self.

Loser.new do |c|
  c.name = "John Doe"
end
# => #<struct Loser id=nil, name=nil>
# Name is not set

Winner.new do |c|
  c.name = "John Doe"
end
# => #<struct Winner id=nil, name="John Doe">
# Hurra! Name is set!

I called it RubyStruct. It was created for the Ruby Whois, but ultimately I found myself using it quite often in my projects and I decided to package it in a single file along with a basic test suite.

Here's the library.

#
# = SuperStruct
#
# SuperStruct is an enhanced version of the Ruby Standard library <tt>Struct</tt>.
#
# Compared with the original version, it provides the following additional features:
# * ability to initialize an instance from Hash
# * ability to pass a block on creation
#
# You can read more at http://www.simonecarletti.com/blog/2010/01/ruby-superstruct/
#
# Category:: Standard
# Package:: SuperStruct
# Author:: Simone Carletti <weppos@weppos.net>
# License:: MIT License
# Source:: http://gist.github.com/271214
#
require 'ostruct'
class SuperStruct < Struct
# Overwrites the standard Struct initializer
# to add the ability to create an instance from a Hash of parameters
# and pass a block to yield on self.
#
# SuperEroe = SuperStruct.new(:name, :nickname)
#
# attributes = { :name => "Pippo", :nickname => "SuperPippo" }
# SuperEroe.new(attributes)
# # => #<struct SuperEroe name="Pippo", nickname="SuperPippo">
#
# SuperEroe.new do |s|
# s.name = "Pippo"
# s.nickname = "SuperPippo"
# end
# # => #<struct SuperEroe name="Pippo", nickname="SuperPippo">
#
def initialize(*args, &block)
if args.first.is_a? Hash
initialize_with_hash(args.first)
else
super
end
yield(self) if block_given?
end
private
def initialize_with_hash(attributes = {})
attributes.each do |key, value|
self[key] = value
end
end
end
if $0 == __FILE__
require 'test/unit'
class SuperStructTest < Test::Unit::TestCase
SuperEroe = Class.new(SuperStruct.new(:name, :supername))
def setup
@klass = SuperEroe
end
def test_initialize_with_block
@klass.new do |instance|
assert_instance_of SuperEroe, instance
assert_kind_of SuperStruct, instance
end
end
def test_initialize_with_hash
instance = @klass.new(:name => "Pippo", :supername => "SuperPippo")
assert_equal "Pippo", instance.name
assert_equal "SuperPippo", instance.supername
end
end
end
view raw super_struct.rb hosted with ❤ by GitHub