Capturing puts in mod_ruby

Hello,

In order to run code samples for the tutorial I am writing, I essentially do
the following:

strIO = StringIO.new
if !input.empty?
input = input.join("\n")+"\n"
input = StringIO.new (input, “r”)
class << strIO; self; end.class_eval do
[‘gets’, ‘getc’, ‘read’].each do |meth|
define_method(meth) do
inStr = input.method(meth).call
puts inStr.chomp # Echo input.
inStr
end
end
end
end

strIO.instance_eval code

This works pretty well. However, what if the code I was trying to run did
something like this?

class Foo
def foo
puts 'Foo#foo was called.'
end
end

Foo.new.foo

This is not doing the StringIO puts, but the Kernel puts instead.
(Actually, it does some sort of special mod_ruby puts which spits the output
straight to the browser, which REALLY screws things up for me.) Anyone know
how to fix something like this? Perhaps I can do something like mod_ruby
does (redefining puts), but how is mod_ruby doing it?

Many thanks for any help,

Chris

Well, I figured it out. Just in case anyone is interested, here is the
complete code I’m using. This executes the code in the code'' parameter (a string), supplying the strings in theinput’’ array as user input when
requested, and it returns the output in a string. (The variable ``@@INPUT’'
is just a string I am using to help me to distinguish output from echoed
input.)

The idea was to create a sufficiently robust execution environment for
running code samples and showing what those code samples do if run on the
command line. (Of course, “sufficiently” is a moving target… or maybe a
shrinking target.) I wanted as few side-effects as possible (since I’m
running this in mod_ruby). Aside from a tutorial, I think this might be
nice to incorporate into a ruby-based wiki… but I don’t know much about
$SAFE levels and all that.

It’s my dream that someday we’ll have a wiki-based documentation of the
built-in classes and standard libraries, along with live code examples, so
that we can all help to keep the docs up-to-date. (The Pickaxe reference
section, while wonderful, is slowly falling out of date… it would be nice
to have a more-or-less “definitive” place where we could keep up-to-date
docs.) Anyway, I need to finish this tutorial first…

So here’s the code. It handles errors (including parse errors), and
all-in-all seems to work pretty well. There are a few sticky areas when
running from mod_ruby… basically, this code acts like you are running it
in mod_ruby (go figure!) instead of on the command line.

def executeCode (code, input)
# Wrap code to catch errors and to stop SystemExit.
code = <<-END_CODE
begin
#{code}
rescue SystemExit
rescue Exception => error
puts error.inspect
end
END_CODE

strIO = StringIO.new

if !input.empty?
  input = input.join("\n")+"\n"
  input = StringIO.new (input, "r")
  class << strIO; self; end.module_eval do
    ['gets', 'getc', 'read'].each do |meth|
      define_method(meth) do |*params|
        inStr = input.method(meth).call(*params)
        puts @@INPUT+inStr.chomp+(@@INPUT.reverse)  #  Echo input.
        inStr
      end
    end
  end
end

#  Pass these methods to strIO:
kernelMethods = ['puts', 'putc', 'gets']

#  Swap out Kernel methods...
kernelMethods.each do |meth|
  Kernel.module_eval "alias __temp__tutorial__#{meth}__ #{meth}"
  Kernel.module_eval do
    define_method(meth) do |*params|
      strIO.method(meth).call(*params)
    end
  end
end

begin
  strIO.instance_eval code
rescue Exception => error  #  Catch parse errors.
  return error.inspect
end

#  ...and swap them back in.
kernelMethods.each do |meth|
  Kernel.module_eval "alias #{meth} __temp__tutorial__#{meth}__"
end

strIO.string

end

Thoughts? Suggestions? Errors?

Chris

It’s my dream that someday we’ll have a wiki-based documentation of the
built-in classes and standard libraries,

See http://www.ruby-lang.org/~rubikitch/RDP-en.cgi?cmd=view;name=RDP-list

It’s fairly incomplete, though I think the Japanese version has more.

James