Hi --

On Mon, 12 Aug 2002, Tom Sawyer wrote:

> i'm creating my own attribute module methods to replace attr_reader,
> attr_writer, and attr_accessor. instead of having a seperate method for
> assignment, i use the same method as the reader, but it checks to see if
> there's an argument passed. if there is than it acts like a setter.
>
> not positive about the names of these yet, but:
>
> class Module
>
>   def Module.attr_rw(name)
>     name = name.to_s
>     class_eval <<-EOS
>       if not method_defined?(:#{name})
>         def #{name}(*args)
>           if args.length > 0
>             return @#{name} = args[0]
>           else
>             if @#{name}
>               return @#{name}
>             else
>               return @#{name} = nil
>             end
>           end
>         end
>       end
>     EOS
>   end
>
>   def Module.attr_ro(name)
>     name = name.to_s
>     class_eval <<-EOS
>       if not method_defined?(:#{name})
>         def #{name}
>           if @#{name}
>             return @#{name}
>           else
>             return @#{name} = nil
>           end
>         end
>       end
>     EOS
>   end
>
> end
>
> is this the right way to go about this? can anyone improve on this code?
> or see any flaws that need to be fixed? i don't have a write only
> version, do i really need it? i don't think i've have used one before.

I would recommend:

1. Don't check for method existence.  Use -w, and let Ruby warn you
   if you're redefining and/or discarding methods.
2. Consolidate some of the logic; I don't think you need that many
   branches.  (Or am I overlooking a scenario?)
3. Consider how you would do the equivalent of this:

       class X
	 attr_reader :thing
	 def thing=(x)
	   @thing = "I am #{x}"
	 end
       end

     I can't spot a way to do it very cleanly (i.e., without either
     using aliases and/or manually rewriting the method that was
     automatically written.)

Here's a somewhat trimmed-down implementation (which I think behaves
the same as yours).  I've added multi-argument handling.

  class Module
    def attr_rw(*names)
      names.each do |name|
	class_eval <<-EOS
	def #{name}(*args)
	  @#{name} = args[0] if args.size > 0
	  @#{name}
	end
	EOS
      end
    end

    def attr_ro(*names)
      names.each do |name|
      class_eval <<-EOS
      def #{name}
	@#{name}
      end
      EOS
    end
    end
  end

  class X
    attr_rw :thing, :other
  end

  x = X.new
  x.thing "Hello"
  puts x.thing

  x.other "Bye"
  puts x.other


David

-- 
David Alan Black
home: dblack / candle.superlink.net
work: blackdav / shu.edu
Web:  http://pirate.shu.edu/~blackdav