[ANN] traits-0.3.0

URLS

   http://raa.ruby-lang.org/search.rhtml?search=traits
   Index of /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:

HISTORY

   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

   <========< 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'){|this| this.name.upcase }
       end

       has('classname'){|this| this.class.name.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
     "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

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

Hello Ara,

would it be possible to have a kind of observer on traits, kind of like Apple/obc/cocoa's KVO?

Ralph "PJPizza" Siegler

···

On Wed, Jun 22, 2005 at 09:18:13PM +0900, Ara.T.Howard wrote:

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:

Ara.T.Howard wrote:
...

  <========< sample/c.rb >========>

...

    class C
      class << self
        has('classname'){|this| this.name.upcase }
      end

      has('classname'){|this| this.class.name.downcase }
    end

Hi, Ara,
Just curious, why wouldn't #instance_eval give you the desided behavior? In that case, you could just use 'self' instead of 'this'.

can you give me a little code snippet to show how you'd like it to look? i'm
imagining something like

   class C
     has 'a', 'default' => 42,
              'pre' => proc{|this| puts "pre a : #{ this.a }"},
              'post' => proc{|this| puts "post a : #{ this.a }"},
   end

   obj = C::new
   obj.a('forty-two')

would result in

   pre a : 42
   post a : forty-two

being printed

or were you thinking more like

   class C
     some_observer = SomeObserver::new

     has 'a', 'default' => 42,
              'observer' => some_observer
   end

and observer.notify(self, value) would be called or something?

cheers.

-a

···

On Wed, 22 Jun 2005, Ralph "PJPizza" Siegler wrote:

On Wed, Jun 22, 2005 at 09:18:13PM +0900, Ara.T.Howard wrote:

URLS

  http://raa.ruby-lang.org/search.rhtml?search=traits
  Index of /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:

Hello Ara,

would it be possible to have a kind of observer on traits, kind of like Apple/obc/cocoa's KVO?

Ralph "PJPizza" Siegler

--

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

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

a better question is why i'm not drinking enough coffee!

look for traits 0.4.0 soon - it has this exact change :wink:

-a

···

On Thu, 23 Jun 2005, Joel VanderWerf wrote:

Ara.T.Howard wrote:
...

  <========< sample/c.rb >========>

...

    class C
      class << self
        has('classname'){|this| this.name.upcase }
      end

      has('classname'){|this| this.class.name.downcase }
    end

Hi, Ara,
Just curious, why wouldn't #instance_eval give you the desided behavior? In that case, you could just use 'self' instead of 'this'.

--

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

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

Ralph "PJPizza" Siegler wrote:

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:

Hello Ara,

would it be possible to have a kind of observer on traits, kind of like Apple/obc/cocoa's KVO?

Ralph "PJPizza" Siegler

I'm not sure what those observers do, but the observable module on RAA can be used orthogonally with any pair of methods #attr and #attr=, as in the following example:

[~] cat foo.rb
require 'observable'
class A
   def foo; 3; end
   def foo=(x); p x; end
   extend Observable
   observable :foo
end

a = A.new

a.when_foo(Object) {|val| p "changing foo to #{val.inspect}"}

a.foo = "zap"

[~] ruby foo.rb
"changing foo to 3"
"zap"
"changing foo to \"zap\""

So presumably this could be used with traits without modifying either library.

···

On Wed, Jun 22, 2005 at 09:18:13PM +0900, Ara.T.Howard wrote:

imagining something like

  class C
    has 'a', 'default' => 42,
             'pre' => proc{|this| puts "pre a : #{ this.a }"},
             'post' => proc{|this| puts "post a : #{ this.a }"},
  end

  obj = C::new
  obj.a('forty-two')

Wow, this aspect-oriented flavor of idea is even *better* than what I was thinking of!

or were you thinking more like

  class C
    some_observer = SomeObserver::new

    has 'a', 'default' => 42,
             'observer' => some_observer
  end

and observer.notify(self, value) would be called or something?

yes, that's what I was thinking, but that AO type idea is even kewler

Ralph "PJPizza" Siegler

···

On Wed, Jun 22, 2005 at 11:36:10PM +0900, Ara.T.Howard wrote:

good because it was giving me fits :wink: bascially it's tough to tell this

   trait 'a', 'default' => 42

from

   trait 'default' => 42

or this

   trait 'a', 'pre' => a_proc

from this

   trait 'pre' => a_proc

because i already allow this list syntax

   traits 'a', 'b', 'c' => 42', 'd' => 'forty-two'

orthogonal is good! i didn't know about that btw... nice.

cheers.

-a

···

On Thu, 23 Jun 2005, Joel VanderWerf wrote:

Ralph "PJPizza" Siegler wrote:

On Wed, Jun 22, 2005 at 09:18:13PM +0900, Ara.T.Howard wrote:

URLS

http://raa.ruby-lang.org/search.rhtml?search=traits
Index of /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:

Hello Ara,

would it be possible to have a kind of observer on traits, kind of like Apple/obc/cocoa's KVO?

Ralph "PJPizza" Siegler

I'm not sure what those observers do, but the observable module on RAA can be used orthogonally with any pair of methods #attr and #attr=, as in the following example:

[~] cat foo.rb
require 'observable'
class A
def foo; 3; end
def foo=(x); p x; end
extend Observable
observable :foo
end

a = A.new

a.when_foo(Object) {|val| p "changing foo to #{val.inspect}"}

a.foo = "zap"

[~] ruby foo.rb
"changing foo to 3"
"zap"
"changing foo to \"zap\""

So presumably this could be used with traits without modifying either library.

--

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

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

Joel,

that's awesome, I only had known about Ruby's included Observer mixin,
not this thing.

thanks very much,

Ralph "PJPizza" Siegler

···

On Thu, Jun 23, 2005 at 05:54:16AM +0900, Joel VanderWerf wrote:

I'm not sure what those observers do, but the observable module on RAA
can be used orthogonally with any pair of methods #attr and #attr=, as
in the following example:

I'm not sure I'd ever use it, but if you're looking for ideas, I think a protocol by which to register observers would be pretty neat.

*reads email about 'observable' library*

I mean, uh, an improvement over the observable library. Something like this, perchance:

class C
  has :a, :observers => true
  has :b, :validators => true
end

c = C.new
c.observe(:a) { |old,new|
  blah
}
c.observe(:a) { |old,new|
  additional_blah
}
c.validate(:b) { |old,new|
  blah?
}

If not blah?, then b is not changed.

I'm not sure what it should do, in the observe case, should the block throw an exception.

Also, if you could think of a good way to allow the object to internally add observers/validators, even if observers isn't true. Ideally, modify the protocol to make use of Ruby access control. Maybe give them individual private methods, such as observe-a and validate-b. Maybe, instead, just make add_observer and add_validator private methods that do the same thing.

Or, yeah, I could just direct these suggestions to the author of 'observable'. :wink:

Devin

Ralph "PJPizza" Siegler wrote:

···

On Thu, Jun 23, 2005 at 05:54:16AM +0900, Joel VanderWerf wrote:

I'm not sure what those observers do, but the observable module on RAA can be used orthogonally with any pair of methods #attr and #attr=, as in the following example:

Joel,

that's awesome, I only had known about Ruby's included Observer mixin,
not this thing.

Thanks! It's especially useful in GUI code, as in the foxtails lib, which uses it to wire up various Fox controls and views with your apps "model" variables.

Devin Mullins wrote:

I'm not sure I'd ever use it, but if you're looking for ideas, I think a protocol by which to register observers would be pretty neat.

*reads email about 'observable' library*

I mean, uh, an improvement over the observable library. Something like this, perchance:

class C
has :a, :observers => true
has :b, :validators => true
end

c = C.new
c.observe(:a) { |old,new|
blah
}
c.observe(:a) { |old,new|
additional_blah
}
c.validate(:b) { |old,new|
blah?
}

If not blah?, then b is not changed.

Currently, observers just get notified after the action has happened. A pre-action hook might be useful. I'll look into it next time I update 'observable'.