Store erb block and alter scope of erb block eval?

Discussion in 'Ruby' started by Steve V, Apr 18, 2005.

  1. Steve V

    Steve V Guest

    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
    Steve V, Apr 18, 2005
    #1
    1. Advertising

  2. On Apr 18, 2005, at 3:54 PM, Steve V wrote:
    > 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>"
    Gavin Kistner, Apr 19, 2005
    #2
    1. Advertising

  3. Steve V

    Steve V Guest


    > 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
    Steve V, Apr 19, 2005
    #3
  4. Steve V

    Steve V Guest


    > 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
    Steve V, Apr 19, 2005
    #4
  5. 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.


    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.)
    --
    "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
    Gavin Kistner, Apr 20, 2005
    #5
  6. Steve V

    Steve V Guest


    >
    > 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
    Steve V, Apr 20, 2005
    #6
  7. Steve V

    Steve V Guest

    > 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
    Steve V, Apr 20, 2005
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. DataBinder.Eval and Eval.

    , Jun 16, 2006, in forum: ASP .Net
    Replies:
    1
    Views:
    513
    Karl Seguin [MVP]
    Jun 16, 2006
  2. Replies:
    1
    Views:
    131
    Ken Cox [Microsoft MVP]
    Oct 6, 2006
  3. Wybo Dekker

    erb and scope

    Wybo Dekker, Dec 11, 2005, in forum: Ruby
    Replies:
    6
    Views:
    85
    James Edward Gray II
    Dec 11, 2005
  4. Dan Thomas

    link to .erb file, from .erb file

    Dan Thomas, Jan 26, 2011, in forum: Ruby
    Replies:
    4
    Views:
    153
    Dan Thomas
    Jan 26, 2011
  5. -berlin.de
    Replies:
    0
    Views:
    130
    -berlin.de
    Jun 30, 2007
Loading...

Share This Page