No clue

Hi --

···

On Sun, 14 Aug 2005, [ISO-8859-1] Simon Kröger wrote:

data.each{ |j, line|
k, v = -2, 0
while (v = line.index(58, k))
   h5[j][line[(k+2)...v].intern] =
     line[(v+2)...(k = line.index(44, v) || line.length)]
end
}

One is loosing readability of code if optimizing for speed
has top priority - even in ruby.

Would you lose much speed if you did ?: and ?, ? :slight_smile:

It's interesting though -- I remember the long thread back in 2000 or
2001 about finding anagrams. The fastest one, I seem to recall, used
pack/unpack (I can't remember the details, though I believe I wrote
it), and raised similar issues.

David

--
David A. Black
dblack@wobblini.net

Ah, ok. In my application, there's a bunch more than 3 possible keys and
they are of differing length. I am in control of the format of the incoming
strings though, and so could modify their format to make them easier/faster
to parse. Any ideas on what would be a more efficient format for
transporting the data?

binary.

(for reference, the original string format was "id: 3, x_position: 39,
y_position: 209, z_position: 39" and in my real application, there's about
twenty different attributes that are in the string.)

Perhaps it would be more efficient to not convert the string into a hash?

All I really need to be able to do is access/display a player's data via
some mechanism, and a player's data should be updated once a second, and
there's up to 400 players. The above was the best way I could come up with
transporting and accessing the data, but perhaps there's a better way of
doing it.

something similar in spirit to this would support many players:

     harp:~ > cat a.rb
     class PlayerData
       ATTRIBUTES = %w(
         id
         x_position
         y_position
         z_position
         foobar
       ).each{|a| attr a}

       FORMAT = 'ifffi'

       class << self
         def create(*a); new(a.flatten.pack(FORMAT)); end
       end

       attr :buffer
       attr :to_s

       def update buffer
         ATTRIBUTES.zip((@buffer = buffer).unpack(FORMAT)) do |a,v|
           instance_variable_set "@#{ a }", v
         end
         @to_s = ATTRIBUTES.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
       end
       alias initialize update
     end

     id, x_position, y_position, z_position, foobar =
       400, 1.0, 2.0, 3.0, 0b101010

     pd = PlayerData::create id, x_position, y_position, z_position, foobar

     p pd
     p pd.id
     p pd.x_position
     p pd.foobar
     puts pd

     begin
       require 'timeout'
       n = 0
       Timeout::timeout(10) do
         loop do
           PlayerData::create id, x_position, y_position, z_position, foobar
           n += 1
         end
       end
     rescue Timeout::Error
       puts "creations per second : #{ n / 10 }"
     end

     harp:~ > ruby a.rb
     #<PlayerData:0xb75cdf04 @x_position=1.0, @foobar=42, @id=400, @buffer="\220\001\000\000\000\000\200?\000\000\000@\000\000@@*\000\000\000", @z_position=3.0, @to_s="id : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42", @y_position=2.0>
     400
     1.0
     42
     id : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     creations per second : 14045

not to mention the message (player data strings) will be an order of
magintitude smaller to pass around - like over a network.

cheers.

-a

···

On Sun, 14 Aug 2005, Joe Van Dyk wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death Like a lamp standing in a strong breeze. --Nagarjuna

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

Joe Van Dyk wrote:

Ah, ok. In my application, there's a bunch more than 3 possible keys
and they are of differing length. I am in control of the format of
the incoming strings though, and so could modify their format to make
them easier/faster to parse. Any ideas on what would be a more
efficient format for transporting the data?

More different keys aren't a problem to this algorithm, but still there
might be a lot better ways to transport the data. One is perhaps a
string that can be 'executed' via 'eval' directly, but that is kind
of hacky.

Using binary data is perhaps the way to go, but of course much harder
to debug.

If you find a fast implementation of xml-rpc (a ruby extension,
written in c) this might combine speed with readability.

(for reference, the original string format was "id: 3, x_position: 39,
y_position: 209, z_position: 39" and in my real application, there's
about twenty different attributes that are in the string.)

Again, this will work with the algorithm, but perhaps not fast enough..

cheers

Simon

Would you lose much speed if you did ?: and ?, ? :slight_smile:

*g*

no, probably not. That's one of the myriad ruby goodies i didn't come
across yet. (i was thinking about ':'[0])

Simon

Thanks! I shall study this code (there's a few new idioms that I
haven't used before) and report back early next week on the
performance improvements.

···

On 8/13/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

> Ah, ok. In my application, there's a bunch more than 3 possible keys and
> they are of differing length. I am in control of the format of the incoming
> strings though, and so could modify their format to make them easier/faster
> to parse. Any ideas on what would be a more efficient format for
> transporting the data?

binary.

> (for reference, the original string format was "id: 3, x_position: 39,
> y_position: 209, z_position: 39" and in my real application, there's about
> twenty different attributes that are in the string.)
>
> Perhaps it would be more efficient to not convert the string into a hash?
>
> All I really need to be able to do is access/display a player's data via
> some mechanism, and a player's data should be updated once a second, and
> there's up to 400 players. The above was the best way I could come up with
> transporting and accessing the data, but perhaps there's a better way of
> doing it.

something similar in spirit to this would support many players:

     harp:~ > cat a.rb
     class PlayerData
       ATTRIBUTES = %w(
         id
         x_position
         y_position
         z_position
         foobar
       ).each{|a| attr a}

       FORMAT = 'ifffi'

       class << self
         def create(*a); new(a.flatten.pack(FORMAT)); end
       end

       attr :buffer
       attr :to_s

       def update buffer
         ATTRIBUTES.zip((@buffer = buffer).unpack(FORMAT)) do |a,v|
           instance_variable_set "@#{ a }", v
         end
         @to_s = ATTRIBUTES.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
       end
       alias initialize update
     end

     id, x_position, y_position, z_position, foobar =
       400, 1.0, 2.0, 3.0, 0b101010

     pd = PlayerData::create id, x_position, y_position, z_position, foobar

     p pd
     p pd.id
     p pd.x_position
     p pd.foobar
     puts pd

     begin
       require 'timeout'
       n = 0
       Timeout::timeout(10) do
         loop do
           PlayerData::create id, x_position, y_position, z_position, foobar
           n += 1
         end
       end
     rescue Timeout::Error
       puts "creations per second : #{ n / 10 }"
     end

     harp:~ > ruby a.rb
     #<PlayerData:0xb75cdf04 @x_position=1.0, @foobar=42, @id=400, @buffer="\220\001\000\000\000\000\200?\000\000\000@\000\000@@*\000\000\000", @z_position=3.0, @to_s="id : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42", @y_position=2.0>
     400
     1.0
     42
     id : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     creations per second : 14045

not to mention the message (player data strings) will be an order of
magintitude smaller to pass around - like over a network.

<snip>

Thanks! I shall study this code (there's a few new idioms that I
haven't used before) and report back early next week on the
performance improvements.

try this one - it's another order of magnitude faster:

     harp:~ > ruby a.rb
     #<PlayerData:0xb75cc0b4 @to_s=nil, @data=[400, 1.0, 2.0, 3.0, 42], @to_bin=nil>
     400
     1.0
     42
     pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     creations per second : 114797

     harp:~ > cat a.rb
     class PlayerData
       class << self
         def create(*a)
           new(a.pack(FORMAT))
         end
       end

       SPEC = [
         %w( pid i ),
         %w( x_position f ),
         %w( y_position f ),
         %w( z_position f ),
         %w( foobar i ),
       ]

       ATTRIBUTES = SPEC.map{|s| s.first}

       FORMAT = SPEC.map{|s| s.last}.join

       SPEC.each_with_index do |spec, ix|
         at, format = spec
         eval <<-src
           def #{ at }
             @#{ at } ||= @data[#{ ix }]
           end
           def #{ at }= value
             raise TypeError unless self.#{ at }.class == value.class
             uncache
             @#{ at } = @data[#{ ix }] = value
           end
         src
       end

       def update buffer
         uncache
         @data = buffer.unpack FORMAT
       end
       alias initialize update
       def uncache
         @to_s = @to_bin = nil
       end
       def to_s
         @to_s ||= ATTRIBUTES.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
       end
       def to_bin
         @to_bin ||= @data.pack(FORMAT)
       end
     end

     id, x_position, y_position, z_position, foobar =
       400, 1.0, 2.0, 3.0, 0b101010

     pd = PlayerData::create id, x_position, y_position, z_position, foobar

     p pd
     p pd.pid
     p pd.x_position
     p pd.foobar
     puts pd

     begin
       require 'timeout'
       n = 0
       sec = 10
       Timeout::timeout(sec) do
         loop do
           PlayerData::create id, x_position, y_position, z_position, foobar
           n += 1
         end
       end
     rescue Timeout::Error
       puts "creations per second : #{ n / sec }"
     end

basically it just unpacks everything and then uses lazy evaluation to get the
values when asked. it's hard to imagine getting faster than this, but someone
will surely prove me wrong :wink:

hth.

-a

···

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

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

Joe Van Dyk wrote:

···

On 8/13/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

      FORMAT = 'ifffi'

If this is going across a network, you might want 'NgggN' instead, so
everybody sees the same byte order.

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

joel is absolutely correct here - the would be a very anoying thing to debug
too....

-a

···

On Mon, 15 Aug 2005, Joel VanderWerf wrote:

Joe Van Dyk wrote:

On 8/13/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

      FORMAT = 'ifffi'

If this is going across a network, you might want 'NgggN' instead, so
everybody sees the same byte order.

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

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

Why do you use 'class << self' here?

Thanks,
Joe

···

On 8/14/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

>> On Sun, 14 Aug 2005, Joe Van Dyk wrote:
<snip>
> Thanks! I shall study this code (there's a few new idioms that I
> haven't used before) and report back early next week on the
> performance improvements.

try this one - it's another order of magnitude faster:

     harp:~ > ruby a.rb
     #<PlayerData:0xb75cc0b4 @to_s=nil, @data=[400, 1.0, 2.0, 3.0, 42], @to_bin=nil>
     400
     1.0
     42
     pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     creations per second : 114797

     harp:~ > cat a.rb
     class PlayerData
       class << self
         def create(*a)
           new(a.pack(FORMAT))
         end
       end

So, say I want to use a class similar to this called
'ApplicationData'. The only changes would be the SPEC.

Would the 'ruby way' be to have a base class (say, 'Data'), and then
have PlayerData and ApplicationData inherit from Data and have them
override the SPEC?

Thanks,
Joe

···

On 8/14/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

>> On Sun, 14 Aug 2005, Joe Van Dyk wrote:
<snip>
> Thanks! I shall study this code (there's a few new idioms that I
> haven't used before) and report back early next week on the
> performance improvements.

try this one - it's another order of magnitude faster:

     harp:~ > ruby a.rb
     #<PlayerData:0xb75cc0b4 @to_s=nil, @data=[400, 1.0, 2.0, 3.0, 42], @to_bin=nil>
     400
     1.0
     42
     pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     creations per second : 114797

     harp:~ > cat a.rb
     class PlayerData
       class << self
         def create(*a)
           new(a.pack(FORMAT))
         end
       end

       SPEC = [
         %w( pid i ),
         %w( x_position f ),
         %w( y_position f ),
         %w( z_position f ),
         %w( foobar i ),
       ]

       ATTRIBUTES = SPEC.map{|s| s.first}

       FORMAT = SPEC.map{|s| s.last}.join

       SPEC.each_with_index do |spec, ix|
         at, format = spec
         eval <<-src
           def #{ at }
             @#{ at } ||= @data[#{ ix }]
           end
           def #{ at }= value
             raise TypeError unless self.#{ at }.class == value.class
             uncache
             @#{ at } = @data[#{ ix }] = value
           end
         src
       end

       def update buffer
         uncache
         @data = buffer.unpack FORMAT
       end
       alias initialize update
       def uncache
         @to_s = @to_bin = nil
       end
       def to_s
         @to_s ||= ATTRIBUTES.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
       end
       def to_bin
         @to_bin ||= @data.pack(FORMAT)
       end
     end

     id, x_position, y_position, z_position, foobar =
       400, 1.0, 2.0, 3.0, 0b101010

     pd = PlayerData::create id, x_position, y_position, z_position, foobar

     p pd
     p pd.pid
     p pd.x_position
     p pd.foobar
     puts pd

     begin
       require 'timeout'
       n = 0
       sec = 10
       Timeout::timeout(sec) do
         loop do
           PlayerData::create id, x_position, y_position, z_position, foobar
           n += 1
         end
       end
     rescue Timeout::Error
       puts "creations per second : #{ n / sec }"
     end

basically it just unpacks everything and then uses lazy evaluation to get the
values when asked. it's hard to imagine getting faster than this, but someone
will surely prove me wrong :wink:

     harp:~ > cat a.rb
     class PlayerData
       class << self
         def create(*a)
           new(a.pack(FORMAT))
         end
       end

Why do you use 'class << self' here?

Thanks,
Joe

Hey Joe,

That's another way to define a set of a class methods.
Another is PlayerData.create(*a)...end I think you can write self.create(*a)..end too.
Anything within the class << self...end block will be a class method.

Julian.

to define class methods. in the context of a class think of it like

   class C

     class << self

       # now i'm in class def scope

     end

     # now i'm in instance def scope

   end

therefore you can

   class C
     class << self
       attr :foobar
       alias barfoo foobar
     end
     attr :foobar
     alias barfoo foobar
   end

and then

   c = C::new

   p C::foobar
   p C::barfoo

   p c.foobar
   p c.barfoo

make sense? the scoping makes things like 'attr' and 'alias' possible in the
class scope.

so, for that method i'm defining a 'create' class method (instance factory)
which is like 'new' but accepts normal arguments and packs them into a buffer.
note that the new/initialize pair take a packed binary string as an argument -
but that is terribly inconvenient for testing. therefore i made the create
method - i makes sense for it to be a class method (vs. a global method)
because only the class knows the encoding of the string : encapsulation.

cheers.

-a

···

On Mon, 15 Aug 2005, Joe Van Dyk wrote:

On 8/14/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

On Sun, 14 Aug 2005, Joe Van Dyk wrote:

<snip>

Thanks! I shall study this code (there's a few new idioms that I
haven't used before) and report back early next week on the
performance improvements.

try this one - it's another order of magnitude faster:

     harp:~ > ruby a.rb
     #<PlayerData:0xb75cc0b4 @to_s=nil, @data=[400, 1.0, 2.0, 3.0, 42], @to_bin=nil>
     400
     1.0
     42
     pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     creations per second : 114797

     harp:~ > cat a.rb
     class PlayerData
       class << self
         def create(*a)
           new(a.pack(FORMAT))
         end
       end

Why do you use 'class << self' here?

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

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

So, say I want to use a class similar to this called
'ApplicationData'. The only changes would be the SPEC.

exactly right.

Would the 'ruby way' be to have a base class (say, 'Data'), and then have
PlayerData and ApplicationData inherit from Data and have them override the
SPEC?

i'd personally use a mixin here:

     harp:~ > cat a.rb
     module Packable
       module ClassMethods
         def lazy_attr at, ix
           module_eval <<-src
             def #{ at }
               @#{ at } ||= @data[#{ ix }]
             end
             def #{ at }= value
               raise TypeError unless self.#{ at }.class == value.class
               uncache
               @#{ at } = @data[#{ ix }] = value
             end
           src
         end
         def spec(list=)
           if list.empty?
             @spec
           else
             @attributes =
             @format = ''
             (@spec = list).each_with_index do |pair, ix|
               at, f = pair
               @attributes << at
               @format << f
               lazy_attr at, ix
             end
           end
         end
         attr 'attributes'
         attr 'format'
         def create(*a)
           new(a.flatten.pack(format))
         end
       end
       module InstanceMethods
         attr 'data'
         def klass
           self.class
         end
         def update buffer
           uncache
           @data = buffer.unpack klass.format
         end
         alias initialize update
         def uncache
           @to_s = @to_bin = nil
         end
         def to_s
           @to_s ||= klass.attributes.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
         end
         def to_bin
           @to_bin ||= @data.pack(klass.format)
         end
       end
       def self::included other
         other.extend ClassMethods
         other.module_eval{ include InstanceMethods }
       end
     end

     class PlayerData
       include Packable

       spec [
         %w( pid i ),
         %w( x_position f ),
         %w( y_position f ),
         %w( z_position f ),
         %w( foobar i ),
       ]
     end

     class ApplicationData
       include Packable

       spec [
         %w( aid i ),
         %w( foobar i ),
         %w( barfoo i ),
       ]
     end

     pid, x_position, y_position, z_position, foobar = 400, 1.0, 2.0, 3.0, 0b101010
     pd = PlayerData::create pid, x_position, y_position, z_position, foobar
     puts pd

     aid, foobar, barfoo = 4, 2, 42
     ad = ApplicationData::create aid, foobar, barfoo
     puts ad

     harp:~ > ruby a.rb
     pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     aid : 4, foobar : 2, barfoo : 42

in general class methods and mixins can be combined in an elegant way to
parameterize classes in a way far more flexible than inheritence.

cheers.

-a

···

On Tue, 16 Aug 2005, Joe Van Dyk wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

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

Isn't
def self.create(*a)
...

clearer than

class << self
  def create(*a)
...

? I mean, they both create a class method, right?

···

On 8/14/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Mon, 15 Aug 2005, Joe Van Dyk wrote:

> On 8/14/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:
>> On Sun, 14 Aug 2005, Joe Van Dyk wrote:
>>
>>>> On Sun, 14 Aug 2005, Joe Van Dyk wrote:
>> <snip>
>>> Thanks! I shall study this code (there's a few new idioms that I
>>> haven't used before) and report back early next week on the
>>> performance improvements.
>>
>> try this one - it's another order of magnitude faster:
>>
>>
>> harp:~ > ruby a.rb
>> #<PlayerData:0xb75cc0b4 @to_s=nil, @data=[400, 1.0, 2.0, 3.0, 42], @to_bin=nil>
>> 400
>> 1.0
>> 42
>> pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
>> creations per second : 114797
>>
>>
>> harp:~ > cat a.rb
>> class PlayerData
>> class << self
>> def create(*a)
>> new(a.pack(FORMAT))
>> end
>> end
>
>
> Why do you use 'class << self' here?

to define class methods. in the context of a class think of it like

   class C

     class << self

       # now i'm in class def scope

     end

     # now i'm in instance def scope

   end

therefore you can

   class C
     class << self
       attr :foobar
       alias barfoo foobar
     end
     attr :foobar
     alias barfoo foobar
   end

and then

   c = C::new

   p C::foobar
   p C::barfoo

   p c.foobar
   p c.barfoo

make sense? the scoping makes things like 'attr' and 'alias' possible in the
class scope.

so, for that method i'm defining a 'create' class method (instance factory)
which is like 'new' but accepts normal arguments and packs them into a buffer.
note that the new/initialize pair take a packed binary string as an argument -
but that is terribly inconvenient for testing. therefore i made the create
method - i makes sense for it to be a class method (vs. a global method)
because only the class knows the encoding of the string : encapsulation.

Wow... that's really impressive. Thanks again, you've been extremely helpful.

···

On 8/15/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Tue, 16 Aug 2005, Joe Van Dyk wrote:

> So, say I want to use a class similar to this called
> 'ApplicationData'. The only changes would be the SPEC.

exactly right.

> Would the 'ruby way' be to have a base class (say, 'Data'), and then have
> PlayerData and ApplicationData inherit from Data and have them override the
> SPEC?

i'd personally use a mixin here:

     harp:~ > cat a.rb
     module Packable
       module ClassMethods
         def lazy_attr at, ix
           module_eval <<-src
             def #{ at }
               @#{ at } ||= @data[#{ ix }]
             end
             def #{ at }= value
               raise TypeError unless self.#{ at }.class == value.class
               uncache
               @#{ at } = @data[#{ ix }] = value
             end
           src
         end
         def spec(list=)
           if list.empty?
             @spec
           else
             @attributes =
             @format = ''
             (@spec = list).each_with_index do |pair, ix|
               at, f = pair
               @attributes << at
               @format << f
               lazy_attr at, ix
             end
           end
         end
         attr 'attributes'
         attr 'format'
         def create(*a)
           new(a.flatten.pack(format))
         end
       end
       module InstanceMethods
         attr 'data'
         def klass
           self.class
         end
         def update buffer
           uncache
           @data = buffer.unpack klass.format
         end
         alias initialize update
         def uncache
           @to_s = @to_bin = nil
         end
         def to_s
           @to_s ||= klass.attributes.inject(''){|s,a| s << "#{ a } : #{ send a }, " }.chop.chop
         end
         def to_bin
           @to_bin ||= @data.pack(klass.format)
         end
       end
       def self::included other
         other.extend ClassMethods
         other.module_eval{ include InstanceMethods }
       end
     end

     class PlayerData
       include Packable

       spec [
         %w( pid i ),
         %w( x_position f ),
         %w( y_position f ),
         %w( z_position f ),
         %w( foobar i ),
       ]
     end

     class ApplicationData
       include Packable

       spec [
         %w( aid i ),
         %w( foobar i ),
         %w( barfoo i ),
       ]
     end

     pid, x_position, y_position, z_position, foobar = 400, 1.0, 2.0, 3.0, 0b101010
     pd = PlayerData::create pid, x_position, y_position, z_position, foobar
     puts pd

     aid, foobar, barfoo = 4, 2, 42
     ad = ApplicationData::create aid, foobar, barfoo
     puts ad

     harp:~ > ruby a.rb
     pid : 400, x_position : 1.0, y_position : 2.0, z_position : 3.0, foobar : 42
     aid : 4, foobar : 2, barfoo : 42

in general class methods and mixins can be combined in an elegant way to
parameterize classes in a way far more flexible than inheritence.

Isn't
def self.create(*a)
...

clearer than

class << self
def create(*a)

not when you end up using 'attr' type meta-programming, having class methods
which refer to class methods, aliases, and other things that complicate the
matter:

   harp:~ > cat b.rb
     class C
       class << self
         attr 'a'
         attr 'b'
         alias class_a a
         alias class_b b
         def class_initialize
           @a, @b = 4, 2
         end
         def class_method
           puts [class_a, class_b].join('')
         end
       end

       self.class_initialize

       attr 'a'
       attr 'b'
       alias inst_a a
       alias inst_b b
       def initialize
         @a, @b = 'forty', 'two'
       end
       def instance_method
         puts [inst_a, inst_b].join('-')
       end
     end

   c = C::new
   C::class_method
   c.instance_method

   harp:~ > ruby b.rb
   42
   forty-two

note how 'attr', 'alias', calls to methods that do not specify a receiver, and
instance variables are all consistently in the class scope here. to me this is
clearer than

   class C
     class << self
       attr 'a'
       attr 'b'
     end
     def self::c
       a + b
     end
   end

i always (o.k. mostly) setup my classes like

   class
     # all constants
     class << self
       # all class stuff
     end
     # all instance stuff
   end

if you use the the 'def self::method' approach you cannot be consistent since
some things don't work that way - like 'attr' and 'alias'. for me it's just
an idiom that makes my code clearer, more consistent, and avoids typing.

? I mean, they both create a class method, right?

yes.

hth.

-a

···

On Tue, 16 Aug 2005, Joe Van Dyk wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

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