On 2/19/06, Adam Shelly <adam.shelly / gmail.com> wrote:
>   The only part I don't really like is the a_init_
> sentinel I had to add to pass Koan9.

Ok, after looking at some of the other solutions, I've refined my solution.
It now handles multiple attributes with initializers.
I think I disagree with Ara about what to do when provided with both a
block and a hash.  With multiple attributes, it makes more sense to me
if  the block acts as a default, rather than an override, so that you
get the following:
t = Class.new {attribute(:a,:b=>4,:c=>2){10*b+c} }.new
[t.a, t.b, t.c] => [42, 4, 2]
To get Ara's preferred behaviour, just add `,&block` before the `and`
on the 4th line.

I notice that most people extended Module, while I extended Object. 
Is there a reason to prefer one over the other?

-Adam

#-------Knowledge.rb
#
class Object
  def attribute *names, &block
    names.each do |name|
      attribute *name.map{|k,v|[k,v]} and next if name.kind_of? Hash
      name,v = name
      class_eval "def #{name};"+
            "@#{name}=(defined?(@#{name}) ? @#{name} : #{name}_init_); end"
      class_eval "def #{name}?; !(self.#{name}.nil?); end"
      class_eval "def #{name}=(v); @#{name}=v; end"
      private; define_method("#{name}_init_", block || proc {v})
    end
  end
end