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