[ANN] traits-0.0.0

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 :wink:

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 <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

CAVEATS

   this library is experimental and subject to change.

enjoy.

-a
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
renunciation is not getting rid of the things of this world, but accepting
that they pass away. --aitken roshi

===============================================================================

Magnificent !

···

--

here are more things in heaven and earth,

horatio, than are dreamt of in your philosophy.

Very cool, this is along the lines of something I've been toying with for
the next version of Nemo (rubyforge.org/projects/nemo). Nemo tags class
attributes (traits) with display-related metadata, then later uses it to
render an html table or html edit form. The trait version might look
something like this:

  class Foo
    mtrait :name, :label=>'Full Name', :required=>true
    mtrait :birthday, :type=>:date, :required=>true
  end

  foo = Foo.new
  foo.name = 'Joe'
  foo.birthday = Date.today

  Editor.new(foo).render

This would display an html form with two required text fields labelled "Full
Name" and "Birthday". Hitting the save button updates the object instance
with the given input, assuming it passes all validations.

I've been wanting to abstract out the metadata definition into a generic
mechanism for defining metadata on any attribute, but if this were added to
traits that would pretty much be it. Thoughts?

Also, how does trains handle inheritance? Let's say we have A < B, then B
adds a few more traits, will A's @__trait data remain unaffected?

- Kevin

Very cool, this is along the lines of something I've been toying with for
the next version of Nemo (rubyforge.org/projects/nemo). Nemo tags class
attributes (traits) with display-related metadata, then later uses it to
render an html table or html edit form. The trait version might look
something like this:

class Foo
   mtrait :name, :label=>'Full Name', :required=>true
   mtrait :birthday, :type=>:date, :required=>true
end

foo = Foo.new
foo.name = 'Joe'
foo.birthday = Date.today

Editor.new(foo).render

This would display an html form with two required text fields labelled "Full
Name" and "Birthday". Hitting the save button updates the object instance
with the given input, assuming it passes all validations.

so mtrait would be a wrapper on trait?

I've been wanting to abstract out the metadata definition into a generic
mechanism for defining metadata on any attribute, but if this were added to
traits that would pretty much be it. Thoughts?

you would want something like

   class Foo
     trait 'a', 'description' => 'metadata', 'fu' => 'bar'
   end

   p Foo::trait_opts('a')

that'd be possible. or did you mean something else?

Also, how does trains handle inheritance? Let's say we have A < B, then B
adds a few more traits, will A's @__trait data remain unaffected?

it handles it. it's __very__ tricky however since

   - can't use @@var since derived classes will clobber
   - can't use @var since each class will simply have own

so instead it uses @vars with a search_path up ancestors. this turns out to
be kinda tricky in the general case....

eg.

   jib:~/eg/ruby > cat a.rb
   require 'traits'

   class A
     trait 'a'
   end

   p A::traits

   class B < A
     trait 'b'
   end

   p B::traits

   p A::traits

   jib:~/eg/ruby > ruby a.rb
   [["a"], ["a="]]
   [["b", "a"], ["b=", "a="]]
   [["a"], ["a="]]

cheers

-a

···

On Tue, 3 May 2005, Kevin Howe wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
renunciation is not getting rid of the things of this world, but accepting
that they pass away. --aitken roshi

===============================================================================

I offer for consideration my ValidForm library:

http://phrogz.net/RubyLibs/rdoc/files/ValidForm_rb.html

Allows you to create fields and validation rules around them, render them out to HTML (with client-side validation of those rules) and then re-validate them server-side in Ruby.

···

On May 3, 2005, at 4:54 PM, Kevin Howe wrote:

Very cool, this is along the lines of something I've been toying with for
the next version of Nemo (rubyforge.org/projects/nemo). Nemo tags class
attributes (traits) with display-related metadata, then later uses it to
render an html table or html edit form. The trait version might look
something like this:

> I've been wanting to abstract out the metadata definition into a generic
> mechanism for defining metadata on any attribute, but if this were added

to

> traits that would pretty much be it. Thoughts?

you would want something like

   class Foo
     trait 'a', 'description' => 'metadata', 'fu' => 'bar'
   end

   p Foo::trait_opts('a')

that'd be possible. or did you mean something else?

Exactly :slight_smile: Then something like Foo::trait_opts.each { |trait,opts| } could
be used to loop through the ordered list of traits.

- Kevin