A simpler alternative is to just use Struct:
# direct
Song = Struct.new :name, :artist, :duration
class Song
# other methods
end
Wait wait wait...I never grokked Struct before.
Is it really just a convenient factory for generating classes with a
set of accessor properties, and a constructor that accepts zero or
more of those properties in order?
Holy crap, I see that Song.class is in fact Class.
I may finally get it! Re-reading the ri documentation for Struct, I
see that the above is sort of what it's trying to say. But the
wording always made me believe that the returned object was some sort
of non-class class. Some half-breed freakiness.
The combination of the name and documentation threw me off before. I
suppose 'Struct' sort of works, but something like
'AttributeClass' (while wordier and not as memorable). So apparently
I have no better suggestion for the name. But for the documentation...
At a minimum, I suggest replacing the first sentence:
"A +Struct+ is a convenient way to bundle a number of attributes
together, using accessor methods, without having to write an explicit
class."
Maybe I'm the only one (am I the only one?) but that makes it sound
like the return value isn't a class. Instead, I suggest something like:
The +Struct+ class provides a convenient way to quickly create a
class that contains a set of attributes. In addition to predefining
the accessor methods for these attributes, the returned class
contains other instance methods for accessing and modifying the
attributes. (These methods are documented below as instance methods
of the Struct class, but are in fact methods available to instances
of the class returned from the call to Struct.new).
For example:
User = Struct.new( :first, :last, :email )
p User.class #=> Class
p User.superclass #=> Struct
nobody = User.new
gk = User.new( 'Gavin', 'Kistner' )
p nobody #=> #<struct User first=nil, last=nil, email=nil>
p gk #=> #<struct User first="Gavin", last="Kistner",
email=nil>
p gk.size #=> 3
gk.email = '(e-mail address removed)'
p gk.members #=> ["first", "last", "email"]
p gk.values #=> ["Gavin", "Kistner", "(e-mail address removed)"]
p gk.values_at(1..2) #=> ["Kistner", "(e-mail address removed)"]
p gk.first #=> "Gavin"
p gk['last'] #=> "Kistner"
class User
def fullname
"#{first} #{last}"
end
end
p gk.fullname #=> "Gavin Kistner"
(I think it's very helpful to have an example usage at the very top
of the documentation, showing a simple usage with a type of object
that everyone understands. Obviously, it doesn't have to be my name
or email, though
Also, the documentation for the Struct#values_at method seems to have
been lifted from the Array#values_at method, without changing the
example code to use a Struct rather than array. The example should be
something like:
Stuff = Struct.new( :a, :b, :c, :d, :e, :f )
chars = Stuff.new( 'a1', 'b1', 'c1', 'd1', 'e1', 'f1' )
p chars.values_at(1, 3, 5) #=> ["b1", "d1", "f1"]
p chars.values_at(-1, -3, -5) #=> ["f1", "d1", "b1"]
p chars.values_at(1..3, 2...5) #=> ["b1", "c1", "d1", "c1", "d1", "e1"]
p chars.values_at(1,7) #=> offset 7 too large for struct(size:6)
(IndexError)
(Aside: Has anyone ever used the #values_at method of the Struct
class? If so, what for?)
cc: ruby-doc mailing list