On Thu, 17 Jun 2004, Florian Weber wrote:

> hi!
>
> i have a design question. for example i have a class which reads a file
> and prints out each line with line numbers. what if now i wanna be able
> to plugin some functionality which sorts the file, depending on some
> conditions (extension, etc) before its passed on to the line numbering
> functionality. the class should be as less tied as possible to the
> sorting
> functionality as possible..
>
> how would you normally design that in ruby?
>
> would you just overwrite the method which calls the file read method
> and passes it to the numbering method?
>
> would you pack an object in between, which would check if the file
> has to be sorted, because the conditions are true, read the file and
> the return the sorted or unsorted content?
>
> basically im wondering if its considered a good practice to overwrite
> methods on the same class or if its kinda hackish.. =)
>
> thanks a lot!
>
> ciao!
> florian

first i'd separate the notion of a file's lines into it's own class - one that
simply loads a file and provides a method to access those lines, but does no
printing.  then i'd design outputter classes that operate on line sets.
somewhere in between you can filter/munge the lines - possible using a class
hierarchy of Mungers, possibly using duck-typed objects which respond_to?
'munge', possibly just using callbacks (Procs).  this uses the last approach:

~ > cat a.rb
class LineSet < Array
   attr :path
   def initialize path
     super()
     @path = path
     replace(IO.readlines(path))
   end
   def munge(*procs)
     c = self.cp
     unless procs.empty?
       procs.each{|pc| c = pc.call(c)}
     else
       c = yield c
     end
     c
   end
   def munge!(*procs, &block)
     replace(munge(*procs, &block))
   end
   def cp
     Marshal.load(Marshal.dump(self))
   end
end
class Outputter
   def initialize lines
     @lines = lines
   end
   def output out
     raise NotImplementedError
   end
end
class NumberedOutputter < Outputter
   def output out = STDOUT
     @lines.each_with_index do |line, idx|
       out << "#{ idx }: #{ line }"
     end
   end
end


lines = LineSet.new __FILE__

reverser = lambda{|ls| ls.reverse}
shrinker = lambda{|ls| ls[0,2]}

puts '----'
munged = lines.munge reverser, shrinker
no = NumberedOutputter.new munged
no.output

puts '----'
lines.munge! shrinker, reverser
no = NumberedOutputter.new lines 
no.output

~ > ruby a.rb
----
0: no.output
1: no = NumberedOutputter.new lines 
----
0:   attr :path
1: class LineSet < Array



-a
--
===============================================================================
| EMAIL   :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE   :: 303.497.6469
| A flower falls, even though we love it; and a weed grows, even though we do
| not love it. --Dogen
===============================================================================