Store erb block and alter scope of erb block eval?

I tried posting this in the Rails list, and didn't get any responses, so
maybe there's someone here that can help me with my problem.

I'm trying to modularize some display functionality within my Rails app. I
need to be able to store an erb block, and then evaluate it 1 or more times
at a later point in time. Two questions here, which are driving me crazy!

1) How can I store an erb block captured using do for later use? Regular
variable assignment doesn't appear to work.

  def content(&block)
      @raw_content = block
  end

The above does not work. However, if I evaluate 'block' within the content
method call, then the evaluation works. If I try to evaluate @raw_content at
a later point through another function it just returns nothing. So how
exactly do I store 'block' for later use?

2) When evaluating 'block', how can I get it to use the variables present
within the scope of the calling function, or at least within the calling
class? As it stands now, it will only take variables that have been defined
in the controller, and not my class.

.rhtml file
<%r = Renderer.new()
r.content do%>
  <%=@sometext%>
<%end%>
<%=r.render()%>

.rb file
class SomeController < ApplicationController
    def blah()
    @sometext = "blah"
    end
end

class Renderer
    def content(&block)
    @sometext = "content"
      buffer = eval("_erbout", block.binding);
    pos = buffer.length
    block.call

    @data = buffer[pos..-1]

    buffer[pos..-1] = ""
  end

  def render()
      @data
  end
end

The above will render "blah", and not "content", even if the definition of
@sometext is removed from the controller, the eval will not take from the
class.

Any help would be greatly appreciated.

Thanks,
Steve

1) How can I store an erb block captured using do for later use? Regular
variable assignment doesn't appear to work.

Works for me:

class Foo
    def set_block( &block ); @meth = block; end
    def call_block( *args ); @meth.call( *args ); end
end

f = Foo.new
f.set_block { |x,y| p x,y }
f.call_block 3,4
#=>3
#=>4

2) When evaluating 'block', how can I get it to use the variables present
within the scope of the calling function, or at least within the calling
class?

require 'erb'

class Foo
  def rhtml=( str )
    @rhtml = ERB.new( str )
  end
  def run1
    localvar = 'run1'
    @rhtml.result( binding )
  end
  def run2
    localvar = 'run2'
    @rhtml.result( binding )
  end
end

f = Foo.new
f.rhtml = '<p><%= localvar %></p>'
p f.run1
#=> "<p>run1</p>"
p f.run2
#=> "<p>run2</p>"

···

On Apr 18, 2005, at 3:54 PM, Steve V wrote:

Works for me:

class Foo
    def set_block( &block ); @meth = block; end
    def call_block( *args ); @meth.call( *args ); end
end

f = Foo.new
f.set_block { |x,y| p x,y }
f.call_block 3,4
#=>3
#=>4

> 2) When evaluating 'block', how can I get it to use the variables
> present
> within the scope of the calling function, or at least within the
> calling
> class?

require 'erb'

class Foo
  def rhtml=( str )
    @rhtml = ERB.new( str )
  end
  def run1
    localvar = 'run1'
    @rhtml.result( binding )
  end
  def run2
    localvar = 'run2'
    @rhtml.result( binding )
  end
end

f = Foo.new
f.rhtml = '<p><%= localvar %></p>'
p f.run1
#=> "<p>run1</p>"
p f.run2
#=> "<p>run2</p>"

Is there something special erb does when you capture a block from an rhtml
page? I tried passing my own variable values to the block within my
class(separate from the controller), and the values are ignored. I think the
heart of the problem is that you're calling ERB.new on a regular string. I
need to capture this information from an rhtml page. The only way I have
found to do that is by using a "do" block.

r.content do%>
  Test <%=@sometext%>
<%end%>

This gives me a block, and not a string. Is there a way to turn the contents
of the returned proc as a string? to_s just gives me the memory location(?)
of the proc object.

The other question is why aren't my variables used for the block call when
I'm in the class? Does it have something to do with the existing scope of
the proc object? Can I change the scope? The only place I can get that
@sometext value to be used from is within the controller. I have tried
defining @sometext within my separate class, and passing that into the call,
but nothing happens.

def content(&block)
  @sometext = "sometext"
  @data = block.call(@sometext)
end

def render()
  return @data
end

The above will just render "Test ", and not use the specified value for
@sometext.

Steve

Is there something special erb does when you capture a block from an rhtml
page? I tried passing my own variable values to the block within my
class(separate from the controller), and the values are ignored.
I think the
heart of the problem is that you're calling ERB.new on a regular string. I
need to capture this information from an rhtml page. The only way I have
found to do that is by using a "do" block.

r.content do%>
  Test <%=@sometext%>
<%end%>

This gives me a block, and not a string. Is there a way to turn
the contents
of the returned proc as a string? to_s just gives me the memory
location(?)
of the proc object.

The other question is why aren't my variables used for the block call when
I'm in the class? Does it have something to do with the existing scope of
the proc object? Can I change the scope? The only place I can get that
@sometext value to be used from is within the controller. I have tried
defining @sometext within my separate class, and passing that
into the call,
but nothing happens.

def content(&block)
  @sometext = "sometext"
  @data = block.call(@sometext)
end

def render()
  return @data
end

The above will just render "Test ", and not use the specified value for
@sometext.

After some more experimenting, I have made a little more progress, but I'm
still not able to use a compiled ERB template.

  <%r.data = %q{%>
    So close! <%=blah%>
  <%}%>

The above rhtml code will get me a string into my class, but it isn't
"\t\tSo close! <%=blah%>\n\t" as expected. Instead it is what appears to be
some type of parsed string: "; _erbout.concat "\n" _erbout.concat "\t\tSo
close! "; _erbout.concat((@blah).to_s); _erbout.concat "\n" _erbout.concat
"\t"; "

I can use eval() on the above, with the binding of the working class which
sort of solves the problem. Can anyone tell me what piece I'm missing here
to keep the code suitable for loading into an ERB template?

Thanks,
Steve

Can you be more specific about what you are doing? I don't understand what you mean by "capture a block from an rhtml page".

Could you provide your full code showing the problem you are having?

(I'm not an ERb expert, so I'm probably missing some way of using it that I've not seen before.)

···

On Apr 18, 2005, at 8:56 PM, Steve V wrote:

Is there something special erb does when you capture a block from an rhtml
page? I tried passing my own variable values to the block within my
class(separate from the controller), and the values are ignored. I think the
heart of the problem is that you're calling ERB.new on a regular string. I
need to capture this information from an rhtml page.

--
"When I am working on a problem I never think about beauty. I only think about how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong."
- R. Buckminster Fuller

Can you be more specific about what you are doing? I don't understand
what you mean by "capture a block from an rhtml page".

Could you provide your full code showing the problem you are having?

(I'm not an ERb expert, so I'm probably missing some way of using it
that I've not seen before.)

Hmm, okay. As you will see from using the below. The <%=@title%> value will
only be taken from the value in the TestController. No matter what I do, I
cannot get the @title variable set in the TableBuilder class to be the one
that is used.

Steve

test.rhtml:
<%tb = TableBuilder.new("Table Title")
tb.header_template do%>
  This is the header template <%=@title%>
<%end%>
<%=tb.render()%>

test_controller.rb:
class TestController < ApplicationController
  def test()
    #uncomment, and the title variable will
    # be used in the template
    #@title = "controller title"
  end
end

class TableBuilder

  def initialize(title = "")
    @title = title
  end

  def header_template(&block)
    @headerTemplate = block

  end

  def render()
    out = ""

    #do table building stuff.
    _erbout = ""
    #the below is taken from capture_helper.rb in
    # rails action_view\helpers
    buffer = eval("_erbout", @headerTemplate)
    pos = buffer.length
    @headerTemplate.call(binding)

    out << buffer[pos..-1]
    buffer[pos..-1] = ""

    #do other table building stuff

    return out
  end
end

Hmm, okay. As you will see from using the below. The <%=@title%>
value will
only be taken from the value in the TestController. No matter what I do, I
cannot get the @title variable set in the TableBuilder class to be the one
that is used.

The primary goal of this all is to be able to use the variable from the
object scope other than the controller. The secondary goal is to be able to
cache a precompiled template. The method above gets me a proc object(I
think). If that is the case, are procs already compiled, or do they need to
be compiled at the time of execution? I wanted to store these as ERB
templates so that I could take advantage of caching them in a compiled
state. But if the procs are already compiled and there is a way to cache
them, then that would work as well.

Steve