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.

cheers.

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