A
Ara.T.Howard
i wasn't drinking enough coffee while coding the 0.3.0 release. this is a
remedy release.
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.4.0
- tweaked writer code so multiple values can be passed to setters
- tweaked method of running blocks to use instance_eval so explicit 'this'
arg is no longer needed (though it can still be used)
0.3.0
added ability of default values to be specified with block for deferred
context sensitive initialization (see sample/c.rb)
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
#
# if a block is specifed as the default the initialization of the default value
# is deferred until needed which makes for quite natural trait definitions. the
# block is passed 'self' so references to the current object can be made. (if
# this were not done 'self' in the block would be bound to the class!)
#
class C
class << self
has('classname'){ name.upcase }
end
has('classname'){ self.class.classname.downcase }
end
class B < C; end
o = C::new
p C::classname
p o.classname
o = B::new
p B::classname
p o.classname
~ > ruby sample/c.rb
42
42
42
"C"
"c"
"B"
"b"
<========< 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 <blue> size <420> shape <square>
<========< 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__
enjoy.
-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
remedy release.
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.4.0
- tweaked writer code so multiple values can be passed to setters
- tweaked method of running blocks to use instance_eval so explicit 'this'
arg is no longer needed (though it can still be used)
0.3.0
added ability of default values to be specified with block for deferred
context sensitive initialization (see sample/c.rb)
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
#
# if a block is specifed as the default the initialization of the default value
# is deferred until needed which makes for quite natural trait definitions. the
# block is passed 'self' so references to the current object can be made. (if
# this were not done 'self' in the block would be bound to the class!)
#
class C
class << self
has('classname'){ name.upcase }
end
has('classname'){ self.class.classname.downcase }
end
class B < C; end
o = C::new
p C::classname
p o.classname
o = B::new
p B::classname
p o.classname
~ > ruby sample/c.rb
42
42
42
"C"
"c"
"B"
"b"
<========< 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 <blue> size <420> shape <square>
<========< 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__
enjoy.
-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================