I wrote this a couple weeks ago
( eigenclass.org ) and was
considering whether it deserves an actual release. The implementation is
simple; taking just 40 lines of code, it allows you to do stuff like
class MyClass < SuperClass.new(:a, :b) # doubly appropriate
def sum
@a + @b
end
end
a = MyClass.new(1, 1)
a # => #<MyClass:0xb7dfd254 @b=1,
@a=1>
a.sum # => 2
The generated class uses normal instance variables unlike Struct.new,
and
creates the accessors for you.
You also get keyword arguments for free:
b = MyClass.new :a => 1, :b => 41
b.sum # => 42
b # => #<MyClass:0xb7dfd024 @b=41,
@a=1>
b.b # => 41
Default values are handled as follows:
class Foo < SuperClass.new(:text, :times) { @times ||= 2 }
def greeting
([âHello, #{@text}â] * times).join(â\nâ)
end
end
Foo.new(âSuperClassâ, 2).greeting # => âHello, SuperClass\nHello,
SuperClassâ
Foo.new(:text => âworldâ).greeting # => âHello, world\nHello, worldâ
Unlike Struct.new, you can use SuperClass to generate classes in the
middle
of the inheritance chain:
class X
attr_reader :foo
def initialize(foo = 1)
@foo = foo
end
end
class Y < SuperClass.new(:bar, :baz, X) {@baz ||= 10;
initialize_super(@baz + 1) }
def fubar; @foo + @baz end
end
Y.new(10, 1).foo # => 2
Y.new(:bar => 1).fubar # => 21
I have an extended implementation that also creates #eql?, #hash and #==
methods with selectable semantics.
Hereâs the basic implementation:
Copyright (C) 2006 Mauricio F. [email protected]
Use and distribution under the same terms as Ruby.
class SuperClass
def self.new_class(accessor_type, *args, &block)
parent = args.pop if Class === args.last
parent ||= Object
unless args.size > 1
raise ArgumentError, âNo point in using SuperClass for a single
argument!â
end
Class.new(parent) do
@initialize_args = args.map{|x| â@#{x}â.intern}
class << self; attr_reader :initialize_args end
case accessor_type
when :ro : attr_reader(*args)
when :rw : attr_accessor(*args)
end
define_method(:initialize) do |*a|
args.each{|name| instance_variable_set("@#{name}", nil) }
if a.size == 1 && Hash === a[0]
args.each{|name| instance_variable_set("@#{name}",
a[0][name.to_sym])}
elsif a.size != args.size
raise ArgumentError,
âwrong number of arguments (#{a.size} for #{args.size})â
else
args.each_with_index{|name, i|
instance_variable_set(â@#{name}â, a[i])}
end
instance_eval(&block) if block
end
if block
super_meth = parent.instance_method(:initialize)
define_method(:initialize_super){|*a|
super_meth.bind(self).call(*a) }
private :initialize_super
end
end
end
def self.new(*args, &block); new_class(:ro, *args, &block) end
def self.new_rw(*args, &block); new_class(:rw, *args, &block) end
end