Store erb block and alter scope of erb block eval?

S

Steve V

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
 
G

Gavin Kistner

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>"
 
S

Steve V

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

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
 
S

Steve V

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
 
G

Gavin Kistner

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.

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.)
 
S

Steve V

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
 
S

Steve V

Hmm said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,054
Latest member
LucyCarper

Latest Threads

Top