URLS http://raa.ruby-lang.org/search.rhtml?search=traits http://codeforpeople.com/lib/ruby/traits ABOUT traits.rb aims to be a better set of attr_* methods and encourages better living through meta-programming and uniform access priciples. traits.rb supercedes attributes.rb. why? the name is shorter ;-) AUTHOR ara [dot] t [dot] howard [at] noaa [dot] gov SAMPLES <========< sample/a.rb >========> ~ > cat sample/a.rb require 'traits' # # defining a trait is like attr_accessor in the simple case # class C trait :t end obj = C::new obj.t = 42 p obj.t ~ > ruby sample/a.rb 42 <========< sample/b.rb >========> ~ > cat sample/b.rb require 'traits' # # multiple traits can be defined at once using a list/array of string/sybmol # arguments # class C traits :t0, :t1 traits %w( t2 t3 ) end obj = C::new obj.t0 = 4 obj.t3 = 2 print obj.t0, obj.t3, "\n" ~ > ruby sample/b.rb 42 <========< sample/c.rb >========> ~ > cat sample/c.rb require 'traits' # # a hash argument can be used to specify default values # class C traits 'a' => 4, :b => 2 end obj = C::new print obj.a, obj.b, "\n" ~ > ruby sample/c.rb 42 <========< sample/d.rb >========> ~ > cat sample/d.rb require 'traits' # # all behaviours work within class scope (metalclass) to define class methods # class C class << self traits 'a' => 4, 'b' => 2 end end print C::a, C::b, "\n" ~ > ruby sample/d.rb 42 <========< sample/e.rb >========> ~ > cat sample/e.rb require 'traits' # # shorhands exit to enter 'class << self' in order to define class traits # class C class_traits 'a' => 4, :b => 2 end print C::a, C::b, "\n" ~ > ruby sample/e.rb 42 <========< sample/f.rb >========> ~ > cat sample/f.rb require 'traits' # # as traits are defined they are remembered and can be accessed # class C class_trait :first_class_method trait :first_instance_method end class C class_trait :second_class_method trait :second_instance_method end # # readers and writers are remembered separatedly # p C::class_reader_traits p C::instance_writer_traits # # and can be gotten together at class or instance level # c_getters, c_setters = C::class_traits p [ c_getters, c_setters ] getters, setters = C::traits p [ getters, setters ] ~ > ruby sample/f.rb ["first_class_method", "second_class_method"] ["first_instance_method=", "second_instance_method="] [["first_class_method", "second_class_method"], ["first_class_method=", "second_class_method="]] [["first_instance_method", "second_instance_method"], ["first_instance_method=", "second_instance_method="]] <========< sample/g.rb >========> ~ > cat sample/g.rb require 'traits' # # another neat feature is that they are remembered per hierarchy # class C class_traits :base_class_method trait :base_instance_method end class K < C class_traits :derived_class_method trait :derived_instance_method end p C::class_traits p K::class_traits ~ > ruby sample/g.rb [["base_class_method"], ["base_class_method="]] [["derived_class_method", "base_class_method"], ["derived_class_method=", "base_class_method="]] <========< sample/h.rb >========> ~ > cat sample/h.rb require 'traits' # # a depth first search path to find defaults # class C trait 'a' => 42 end class K < C; end k = K::new p k.a # # once assigned this is short-circuited # k.a = 'forty-two' p k.a ~ > ruby sample/h.rb 42 "forty-two" <========< sample/i.rb >========> ~ > cat sample/i.rb require 'traits' # # getters and setters can be defined separately # class C rtrait :r end class D wtrait :w end # # defining a reader trait still defines __public__ query and __private__ writer # methods # class C def using_private_writer_and_query p r? self.r = 42 p r end end C::new.using_private_writer_and_query # # defining a writer trait still defines __private__ query and __private__ reader # methods # class D def using_private_reader p w? self.w = 'forty-two' p w end end D::new.using_private_reader ~ > ruby sample/i.rb false 42 false "forty-two" <========< sample/j.rb >========> ~ > cat sample/j.rb require 'traits' # # getters delegate to setters if called with arguments # class AbstractWidget class_trait 'color' => 'pinky-green' class_trait 'size' => 42 class_trait 'shape' => 'square' trait 'color' trait 'size' trait 'shape' def initialize color self.class.color size self.class.size shape self.class.shape end def inspect "color <#{ color }> size <#{ size }> shape <#{ shape }>" end end class BlueWidget < AbstractWidget color 'blue' size 420 end p BlueWidget::new ~ > ruby sample/j.rb color size <420> shape <========< sample/k.rb >========> ~ > cat sample/k.rb require 'traits' # # the rememberance of traits can make generic intializers pretty slick # class C # # define class traits with defaults # class_traits( 'a' => 40, 'b' => 1, 'c' => 0 ) # # define instance traits whose defaults come from readable class ones # class_rtraits.each{|ct| instance_trait ct => send(ct)} # # any option we respond_to? clobbers defaults # def initialize opts = {} opts.each{|k,v| send(k,v) if respond_to? k} end # # show anything we can read # def inspect self.class.rtraits.inject(0){|n,t| n += send(t)} end end c = C::new 'c' => 1 p c ~ > ruby sample/k.rb 42 CAVEATS this library is experimental and subject to change.