On Wed, Feb 19, 2003 at 05:08:19PM +0900, Yukihiro Matsumoto wrote:
> Hi,
> 
> In message "Re: How to test for existence of instance variable?"
>     on 03/02/19, Paul Brannan <pbrannan / atdesk.com> writes:
> 
> |Creating an attribute reader and not initializing the attribute (either
> |with @foo= or with self.foo=) is a bug.  The user should never see this
> |warning except for buggy code.
> 
> I thought it is needed just because
> 
>   @foo ||= []
> 
> trick cannot be done for attributes without causing warning.

???

batsman@tux-chan:/tmp$ irb --simple-prompt
/usr/lib/ruby/1.6/irb/context.rb:112: warning: method redefined; discarding old irb_name
/usr/lib/ruby/1.6/irb/context.rb:176: warning: method redefined; discarding old math_mode=
/usr/lib/ruby/1.6/irb/context.rb:188: warning: method redefined; discarding old use_readline=
/usr/lib/ruby/1.6/irb/context.rb:177: warning: instance variable @math_mode not initialized
/usr/lib/ruby/1.6/irb/context.rb:151: warning: instance variable @use_tracer not initialized
>> class A; def foo; @foo ||= []; end; end
=> nil
>> A.new.foo
=> []

If you mean that attr_* won't work with default values, we can use
something like the following:

batsman@tux-chan:/tmp$ set | grep RUBYOPT
RUBYOPT=-w
batsman@tux-chan:/tmp$ expand -t 2 z.rb

module AttrWithDefault
  def attr_reader_with_default(sym, val)
    instance_eval <<-EOF
    class << self
      attr_accessor "_#{sym.to_s}_".intern
      attr_reader :__symlist__
    end
    @__symlist__ ||= []
    @__symlist__.push( [self,  "#{sym}"] )
    EOF
    meth = self.method "_#{sym.to_s}_=".intern
    meth.call( val )

    module_eval <<-EOF
    def #{sym.to_s}
      @#{sym.to_s} ||= self.class._#{sym.to_s}_
    end
    EOF

    if self.singleton_methods.grep("inherited") == []
      class << self
        def inherited sub
          ancestors.each do |klass|
            begin
              assoc = klass.__symlist__
              assoc.each do |x|
                #puts "Redefining #{x[1]} from #{sub} using #{x[0]}._#{x[1]}_"
                sub.module_eval <<-EOF
                def #{x[1]}
                  @#{x[1]} ||= #{x[0]}._#{x[1]}_
                end
                EOF
              end
            rescue NameError
            end
          end
        end
      end
    end
  end
end

class A
  extend AttrWithDefault
  attr_reader_with_default :foo, []
  attr_reader_with_default :bar, 'test'

  def inspect
    str = "#{self.class}:"
    instance_variables.each do |iv|
      str << " #{iv} => "
      str << instance_eval("#{iv}").inspect
    end
    str
  end
end

a = A.new
p a
a.foo
p a

class B < A
  attr_reader_with_default :bar2, 'bla'
end

class C < B
end

b = B.new
p b
b.foo
b.bar
p b


c = C.new
p c
c.foo
c.bar
c.bar2
p c
batsman@tux-chan:/tmp$ ruby z.rb
A:
A: @foo => []
B:
B: @bar => "test" @foo => []
C:
C: @bar => "test" @bar2 => "bla" @foo => []

I am not sure about what default value to take when several are
specified at different points along the inheritance chain.

-- 
 _           _                             
| |__   __ _| |_ ___ _ __ ___   __ _ _ __  
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \ 
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
	Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Sigh.  I like to think it's just the Linux people who want to be on
the "leading edge" so bad they walk right off the precipice.
	-- Craig E. Groeschel