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 ;-) HISTORY 0.1.0 completely reworked impl so NO parsing of inspect strings is required - it's all straight methods (albeit quite confusing ones) now. the interface is unchanged. 0.0.0 initial version 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 o = C::new o.t = 42 p o.t # # and can be made even shorter # class B; has :x; end o = B::new o.x = 42 p o.x ~ > ruby sample/a.rb 42 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 has :t0, :t1 has %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 has 'a' => 4, :b => 2 end o = C::new print o.a, o.b, "\n" # # and these traits are smartly inherited # class K < C; end o = K::new o.a = 40 p( o.a + o.b ) # note that we pick up a default b from C class here since it # has not been set o.a = 42 o.b = nil p( o.b || o.a ) # but not here since we've explicitly set it to nil ~ > ruby sample/c.rb 42 42 42 <========< sample/d.rb >========> ~ > cat sample/d.rb require 'traits' # # all behaviours work within class scope (metal/singleton-class) to define # class methods # class C class << self traits 'a' => 4, 'b' => 2 end end print C::a, C::b, "\n" # # singleton methods can even be defined on objects # class << (a = %w[dog cat ostrich]) has 'category' => 'pets' end p a.category # # and modules # module Mmmm class << self; trait 'good' => 'bacon'; end end p Mmmm.good ~ > ruby sample/d.rb 42 "pets" "bacon" <========< sample/e.rb >========> ~ > cat sample/e.rb require 'traits' # # shorhands exit to enter 'class << self' in order to define class traits # class C class_trait 'a' => 4 c_has :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 # p C::class_traits p C::traits ~ > 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 is used to find defaults # class C has '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 has_r :r end class D has_w :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 iff 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 <========< sample/l.rb >========> ~ > cat sample/l.rb require 'traits' # # even defining single methods on object behaves # a = [] class << a trait 'singleton_class' => class << self;self;end class << self class_trait 'x' => 42 end end p a.singleton_class.x ~ > ruby sample/l.rb 42 CAVEATS this library is experimental and subject to (eg. will) change.