  class Module
#--{{{
  #
  # attributes.rb is a set of meta-programming functions designed to be better
  # than the built-in attr_* functions
  #

  #
  # defines one, or more, instance reader attributes within a class/module.  a
  # __private__ writer method will also be defined for use within the class
  # along with a public query method.
  # 
  #   eg.
  # 
  #     class C
  #       reader_attribute 'a'
  #     
  #       def initialize
  #         a 42               # private setter called via getter
  #         # self.a = 42      # can also call this way 
  #       end
  #     end
  #     
  #     obj = C::new
  #     p obj.a                # public getter => 42
  #     p obj.a?               # public query => true 
  #     
  # note that reader methods - iff called with any argument/block - delegate to the
  # writer method.  that is to say the following are equivalent
  #     
  #       def initialize
  #         a 42          # calls 'self.a = 42'
  #       end
  #     
  #       def initialize
  #         self.a = 42
  #       end
  #     
  # also note that any reader_attributes defined are available as a method of the
  # class so     
  # 
  #   p C::reader_attributes #=> ["a"]
  # 
  # and if later
  # 
  #     class C
  #       reader_attribute 'a', 'b'
  #     end
  # 
  # then
  # 
  #   p C::reader_attributes #=> ["a", "b"]
  # 
  # the method may be called in several ways
  # 
  #   class C
  #     reader_attributes %w( a b c )
  #   end
  # 
  # is the same as
  # 
  #   class C
  #     reader_attributes 'a', 'b', 'c' 
  #   end
  # 
  # is the same as (note singlular)
  # 
  #   class C
  #     reader_attribute 'a'
  #     reader_attribute 'b'
  #     reader_attribute 'c' 
  #   end
  # 
  # is the same as
  # 
  #   class C
  #     reader_attribute :a
  #     reader_attribute :b
  #     reader_attribute :c 
  #   end
  # 
  # is the same as
  # 
  #   class C
  #     reader_attributes :a, :b, :c
  #   end
  # 
    def reader_attributes(*names)
#--{{{
      @@reader_attributes ||= []
      unless names.empty?
        names.flatten.each do |name|
          @@reader_attributes << "#{ name }"
          getter = "#{ name }"
          unless instance_methods.include? getter
            code = <<-code
              def #{ name }(*a, &b)
                unless a.empty?
                  send('#{ name }=', *a, &b)
                else
                  @#{ name }
                end
              end
            code
            module_eval code
          end

          setter = "#{ name }="
          unless instance_methods.include? setter
            code = <<-code
              def #{ name }= value
                @#{ name } = value
              end
              private '#{ name }='.intern
            code
            module_eval code
          end

          query = "#{ name }?"
          unless instance_methods.include? query
            code = <<-code
              alias #{ name }? #{ name }
            code
            module_eval code
          end
        end
      end
      @@reader_attributes.uniq!
      @@reader_attributes
#--}}}
    end
    alias reader_attribute reader_attributes

  #
  # defines one, or more, instance writer attributes within a class/module.  a
  # __private__ reader method will also be defined for use within the class.
  # 
  #   eg.
  # 
  #     class C
  #       writer_attribute :a
  #     
  #       def show_private_a 
  #         a
  #       end
  #     end
  #     
  #     obj = C::new
  #     obj.a 42               # same as obj.a = 42
  #     p obj.show_private_a   #=> 42
  #     
  # also note that any writer_attributes defined are available as a method of the
  # class so     
  # 
  #   p C::writer_attributes #=> ["a="]
  # 
  # the format is suitable for usage with Object#send so one can
  #
  #   C::writer_attributes.each{|wa| obj.send wa, some_value}
  # 
    def writer_attributes(*names)
#--{{{
      @@writer_attributes ||= []
      unless names.empty?
        names.flatten.each do |name|
          @@writer_attributes << "#{ name }="
          getter = "#{ name }"
          unless instance_methods.include? getter
            code = <<-code
              def #{ name }(*a, &b)
                unless a.empty?
                  send('#{ name }=', *a, &b)
                else
                  @#{ name }
                end
              end
              private '#{ name }'.intern
            code
            module_eval code
          end

          setter = "#{ name }="
          unless instance_methods.include? setter
            code = <<-code
              def #{ name }= value
                @#{ name } = value
              end
            code
            module_eval code
          end

          query = "#{ name }?"
          unless instance_methods.include? query
            code = <<-code
              alias #{ name }? #{ name }
              private '#{ name }?'.intern
            code
            module_eval code
          end
        end
      end
      @@writer_attributes.uniq!
      @@writer_attributes
#--}}}
    end
    alias writer_attribute writer_attributes

  #
  # defines one, or more, instance reader/writer attributes within a class/module
  #
  # see docs for reader_attributes/writer_attributes
  #
    def attributes(*names)
#--{{{
      reader_attributes(*names) + writer_attributes(*names)
#--}}}
    end
    alias attribute attributes

  # 
  # exists as a shorthand for
  # 
  #   class C
  #     class << self
  #       reader_attribute 'a'
  #     end
  #   end
  # 
  # that is to say it creates a class accessor.  these accessors are available using
  # the method without arguments.  eg.
  # 
  #   p C::class_reader_attributes  #=> ["a"]
  # 
  # in all other respects this is like reader_attributes
  # 
    def class_reader_attributes(*names)
#--{{{
      class << self; self; end.instance_eval{ reader_attributes(*names) }
#--}}}
    end
    alias class_reader_attribute class_reader_attributes

  #
  # the class version of writer_attributes.  see docs for
  # class_reader_attributes and writer_attributes
  #
    def class_writer_attributes(*names)
#--{{{
      class << self; self; end.instance_eval{ writer_attributes(*names) }
#--}}}
    end
    alias class_writer_attribute class_writer_attributes

  #
  # the class version of attributes.  see docs for class_attributes and
  # attributes
  #
    def class_attributes(*names)
#--{{{
      class << self; self; end.instance_eval{ attributes(*names) }
#--}}}
    end
    alias class_attribute class_attributes
#--}}}
  end

  class Object
    def attributes
      self.class.attributes if self.class.respond_to? 'attributes'
    end
    def reader_attributes
      self.class.reader_attributes if self.class.respond_to? 'reader_attributes'
    end
    def writer_attributes
      self.class.writer_attributes if self.class.respond_to? 'writer_attributes'
    end
    def class_attributes
      self.class.class_attributes if self.class.respond_to? 'class_attributes'
    end
  end
