Hi--

I managed to generalize my 'rubytest' this afternoon into a nice
general purpose comment code block runner. It defaults to unit tests,
but you can use if for any variety of embedded code such as examples.
I'm sure this can be improved upon. For instance the current version
doesn't deal with #-style comments, but only =begin...=end blocks. Let
me know if you have any other suggestions.

I do have one pressing issue though. I came up with two ways to run the
code. 1) using eval, and 2) using a pipe. The first is better b/c I can
maintain the line numbers, so that error reports point to the right
place. OTOH the second way allows all the normal command line options
that ruby handles to be passed along. I would like to achieve both of
these rather than one or the other. Both methdos are presented in the
code below. Any ideas on how to fix?

Hope you all like and find this useful. It will be included in the next
release of my build tools.

Thanks,
T.


# rubyinline

#! /usr/bin/ruby1.8

class InlineRunner

  # This runs the commented code block directly.
  # This has an advantage in that the line numbers
  # can be maintained.

  def run_eval( fname, block='test' )
    code, offset = extract_block( fname )

    require 'test/unit' if block == 'test'
    require fname

    eval code, TOPLEVEL_BINDING, File.basename(fname), offset
  end

  # This runs the commented code block via a pipe.
  # This has an advantage in that all the parameters
  # that can be passed to ruby can be passed to rubyinline.

  def run_pipe( fname, block='test' )
    code, offset = extract_block( fname, block )

    code = "require 'test/unit'\n\n" + code if block == 'test'
    code = "require '#{fname}'\n\n" + code

    cmd = ['ruby', *ARGV].join(' ')

    result = IO.popen(cmd,"w+") do |ruby|
      ruby.puts code
      ruby.close_write
      puts ruby.read
    end
  end

  # Show rubyinline help.

  def help
    helpstr = `ruby --help`
    helpstr.sub!('ruby', 'rubyinline')
    puts helpstr
  end

private

  #

  def pattern( block )
    b = Regexp.escape( block )
    /^=begin\s+#{b}.*?\n(.*)\n=end/mi
  end

  #

  def extract_block( fname, block='test' )
    code = File.read( fname )
    md = pattern( block ).match( code )
    code = md ? md[1] : nil
    unless code
      puts "Code block not found -- #{block}"
      exit 0 #return nil
    end
    offset = code.split(/\n/).size - code.split(/\n/).size - 1
    return code, offset
  end

end

if $0 == __FILE__

  irun = InlineRunner.new

  if ARGV.delete('--help')
    irun.help
    exit 0
  end

  if i = ARGV.index('-b')
    block = ARGV[i+1].strip
    ARGV[i+1,1] = nil
    ARGV.delete('-b')
  else
    block = 'test'
  end

  file = ARGV.pop
  irun.run_eval(file, block)
  #irun.run_pipe(file, block)

end