lib/rbench/runner.rb



module RBench
  class Runner
    attr_accessor :columns, :items, :times, :width
    
    def initialize(times)
      @width = 0
      @times = times
      @columns = []
      @items = []
    end
    
    def run(&block)
      
      # initiate all the columns, groups, reports, and summaries on top level.
      self.instance_eval(&block)
      
      # the groups has not run just yet, but when they do, we really only want
      # to make them initiate their reports, not run them (just yet)
      # when we only have two levels, _every_ report should now be initialized.
      @items.each{|item| item.prepare if item.is_a?(Group)}
      
      # We are putting the summary to the back if its there.
      @items << @items.shift if @items.first.is_a?(Summary)
      
      # if on columns were set, create a default column
      column(:results, :title => "Results") if @columns.empty?
      
      # since we are about to start rendering, we put out the column-header
      puts header
      
      # now we are ready to loop through our items and run!
      items.each { |item| item.run if item.respond_to?(:run) }
      
      # returning self so people can output it in different formats.
      self
    end
    
    def format(options={})
      @width = options.delete(:width) || @width
    end
    
    def column(name,options={})
      @columns << Column.new(self,name,options)
    end
    
    def group(name,times=nil,&block)
      @items << Group.new(self,name,times,&block)
    end
    
    def report(name,times=nil,&block)
      # create an anonymous group, or add it to the last open group.
      group(nil) unless @items.last.is_a?(Group) && !@items.last.block
      # now create the report on the last group
      @items.last.report(name,times,&block)
    end
    
    def summary(name)
      # adding the summary to the front, so it is easier to put it last later.
      @items.unshift(Summary.new(self,nil,name)) unless @items.detect{|i| i.is_a?(Summary)}
    end
    
    ##
    # convenience-methods
    ##
    
    def groups
      @items.select{|item| item.is_a?(Group) }
    end

    def reports
      # we now want _all_ reports, also those that are part of subgroups
      groups.map{|g| g.items.select{|item| item.is_a?(Report) } }.flatten
    end
    
    ##
    # for rendering text. pull out in separate module when to_xml and to_html is in place
    ##
    
    def newline
      "\n"
    end
    
    def header
      " " * desc_width + @columns.map {|c| c.to_s }.join + newline
    end
    
    def desc_width
      @desc_width ||= [items.map{|i| (i.items.map{|r| r.name} << i.name) }.flatten.map{|i| i.to_s.length}.max+8,@width-columns_width].max
    end
    
    def columns_width
      @columns.inject(0){ |tot,c| tot += (c.to_s.length) }
    end
    
    def width(value=nil)
      header.length-1
    end

    def separator(title=nil,chr="-",length=width)
      title ? chr*2 + title + chr * (width - title.length - 2) : chr * length
    end
    
    def to_s
      out = " " * desc_width + @columns.map {|c| c.to_s }.join + newline
      out << @items.map {|item| item.to_s}.join
    end
    
  end
end