On 8/18/05, Gavin Kistner <gavin / refinery.com> wrote:
> On Aug 18, 2005, at 8:47 AM, Gavin Kistner wrote:
> > How would I patch ERB so that puts and print were aliased to
> > concatenate onto the current eoutvar? This aliasing should be setup
> > at the beginning of #result, and removed at the end of #result.
> 
> Here's my horrific hack that does it. It's particularly hacky because
> it requires that the 'eoutvar' be set to have a @ at the beginning of
> the name (an instance variable). The real solution I suppose requires
> a change to the ERB::Compiler#compile method, to replace calls to
> puts and print with the local variable concatenation inline. But that
> was beyond my foo.

<snip>

How about this? Still somewhat hacky, but maybe less breakable. It
should work unless you supply a binding in the Kernel module context;
the new #puts is defined in Object.

-----

require 'erb'

class Object
  # Object's puts overrides Kernel's.
  # Use a thread-local default output if available.
  def puts(*args)
    if Thread.current[:default_output]
      Thread.current[:default_output].puts(*args)
    else
      super
    end
  end
  # Probably should do the same with Kernel#print, #p, etc.
end

# This module turns concatenationability (<<) into writability.
# We'll use it on the eoutvar to let it be used as an IO, too.
module Writeable
  def write(other)
    self << other
    other.size
  end
  def puts(*other)
    other.each{|o| self << (o[-1] == ?\n ? o : o + "\n") }
    nil
  end
  def print(other)
    self << other
    nil
  end
end

class ERB
  def set_eoutvar(compiler, eoutvar = '_erbout')
    compiler.put_cmd = "#{eoutvar}.concat"

    cmd = []
    cmd.push "#{eoutvar} = ''"
    cmd.push "#{eoutvar}.extend Writeable"
    cmd.push "Thread.current[:default_output] = #{eoutvar}"
    
    compiler.pre_cmd = cmd

    cmd = []
    cmd.push "Thread.current[:default_output] = nil"
    cmd.push(eoutvar)

    compiler.post_cmd = cmd
  end
end