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 |