29 April 2008
andEloy shows how to set up a class with default attributes in a single line of code.
29 April 2008
andEloy shows how to set up a class with default attributes in a single line of code.
.flatten!
superklass = attrs.shift if attrs.first.is_a?(Class)
klass = (superklass.nil? ? Class.new : Class.new(superklass))
default_values, attrs = attrs.first, attrs.first.keys if attrs.first.is_a? Hash
# if the klass already contains @__attrs then create a new array, add the existing ones and then the new ones
attrs = (klass.instance_variable_get(:@__attrs).dup + attrs).flatten unless klass.instance_variable_get(:@__attrs).nil?
default_values = klass.instance_variable_get(:@__default_values).merge(default_values) unless klass.instance_variable_get(:@__default_values).nil?
klass.instance_variable_set(:@__attrs, attrs)
klass.instance_variable_set(:@__default_values, default_values)
klass.class_eval do
attr_accessor *attrs
if args.empty?
if default_values = self.class.instance_variable_get(:@__default_values)
default_values.each {|attr, value| send(" =", value) }
end
else
self.class.instance_variable_get(:@__attrs).each_with_index {|attr, index| send(" =", args[index]) }
end
end
# inherit a clone of the @__attrs & @__default_values ivars
klass.instance_variable_set(:@__attrs, @__attrs.dup)
klass.instance_variable_set(:@__default_values, @__default_values.dup) if @__default_values
eval "def (*attrs); Object( , attrs); end" unless klass.name.empty?
end
end
klass
end
if $0 == __FILE__
(:foo, :bar)
end
(:baz, :bla)
end
(:foo => 'foo', :bar => 'bar')
end
(:baz => 'baz')
end
describe "Foo" do
before do
@klass = Object(:foo, :bar)
end
it "should return a new class which holds the attributes, that should be defined, in an array" do
@klass.instance_variable_get(:@__attrs).should == [:foo, :bar]
end
it "should take the attributes as initialization params and assign them as instance variables" do
should_respond_to_and_work(@klass, :foo, :bar)
end
it "should inherit the attributes defined in a superclass" do
Foo.instance_variable_get(:@__attrs).should == [:foo, :bar]
end
it "should create a new method to create a subclass with extra attributes" do
Bar.instance_variable_get(:@__attrs).should == [:foo, :bar, :baz, :bla]
end
it "should take the attributes as initialization params and assign them as instance variables in a subclass with extra attributes" do
should_respond_to_and_work(Bar, :foo, :bar, :baz, :bla)
end
it "should also take a hash as default values" do
baz = Baz.new
[baz.foo, baz.bar].should == ['foo', 'bar']
end
it "should also take a hash as default values and add them to the ones of the superclass" do
daz = Daz.new
[daz.foo, daz.bar, daz.baz].should == ['foo', 'bar', 'baz']
end
private
string_attrs = attrs.map {|a| a.to_s }
obj = klass.new(*string_attrs)
attrs.each_with_index do |attr, index|
obj.should.respond_to(attr)
obj.send(attr).should == string_attrs[index]
end
end
end
end
attrs