Topic: render_to_string from a helper

I understand that render and render_to_string are methods on ActionController, not ActionView.  Howerver, it's perfectly fine to code something like this:

<%= render :partial => 'forum/show_post', :collection => @forum_thread.forum_posts %>
From a view.  So that's some erb magic, I guess.

But, let's assume I've got a bit of logic that is associated with the partial render, and let's assume I want to reuse this bit of logic in several places. So I might as well just DRY up the render call, and stick it into a helper.  Well one way of doing that would be to call render_to_string from inside the helper -- while building the ultimate output string.

<%= my_render :partial => 'forum/show_post', :collection => @forum_thread.forum_posts %>
and define my_render in the proper helper file.  But in that method, I can't make any calls to render() or render_to_string(), because those methods don't live on ActionView, but on ActionController.

So quite obviously, erb does some kind of magic that allows the code to run from the proper controller object.  That would make sense, actually, because now that I think about it, you have access to all the variables, objects and functions defined on the controller from your associated view. 

Perhaps all I have to do is pass a self reference into the my_render helper:

<%= my_render self, ... %>
Or maybe instead of putting the code into the helper, stick it into the controller?  But that is kind of ugly, because it kind of violates the MVC abstraction... right?  I mean, we would not stoop so low as to stick output related code into a controller, would we?

Is there a cleaner way to do this?  Can someone help me think about it?  Perhaps I can make use of yield inside the my_render method?

Last edited by Slurpy (2006-12-20 12:15:43)

Re: render_to_string from a helper

IIRC, you can call render from inside a helper method because it is just like calling it from inside the view ("self" is actually the same in the view and in the helper because the helper is a module which is loaded into the view object). The render method in the view actually returns a string already, so it behaves a little different from the controller. So you should be able to do this:

<%= render_forum_posts(@forum_thread) %>

# in helper
def render_forum_posts(forum_thread)
  # ....
  render :partial => 'forum/show_post', :collection => forum_thread.forum_posts %>
end


I haven't actually tested the above, but it is my understanding on how it works.

Railscasts - Free Ruby on Rails Screencasts

Re: render_to_string from a helper

Yeah -- it appears that render_to_string isn't callable from the view, but you can call render.  Go figure.

I got it to work with using yield too -- but it was not necessary, as you say you can call render from inside the helper.  I kind of like the yield way for some reason.  So now the code looks like so from the view:

<%= admin_panel { render :partial => "admin_threads" } -%>

In the helper:

  def admin_panel(url_params = @params)
 
    return if !is_admin?
 
    out = '<div class="admin-div" id="forum-admin">'
 
    if session[:show_admin_panel]
      out += yield
    end
   
    out += "<p>" + gen_remote_link("[Admin]", "title", url_params.merge({:toggle_admin => true}))
    out += '</p></div>'
   
  end

above gen_remote_link being my own wrapper for a lengthy call to link_to_remote

Re: render_to_string from a helper

Oh.  You just said something that clarifies things further...

"render from the view already returns a string"

Of course!  The view render isn't the same method as the ActionController::Base render.  I just happen to be reading the online documentation for ActionController::Base.  Which is where my confusion probably set in. 

I guess.  smile

What is interesting though, is that I can't seem to find documentation on ActionView::Base render at http://api.rubyonrails.org

Wonder why that is...  I assume it takes all the same args as ActionController::Base, but the distinction between what they return ought to be documented.  A light (or two) would have gone off in my head had I seen an alternatieve method implementation from the view.

Of course maybe this just isn't the "ruby way" of thinking.  For all I know it's a single method someplace that is mixed in, and it figures out from which module / class (or whatever) it's getting called from, and then either outputs to a string, or outputs to the returning document body.

Arg!

Re: render_to_string from a helper

I just looked it up in the source. It's a completely separate method defined in ActionView, they just chose not to document it for some reason or another - probably since it behaves roughly the same as ActionController. Thorough documentation is one thing Rails is lacking and it can be rather annoying.

BTW, you may want to move the HTML out of the helper method and into a partial.

def admin_panel(url_params = @params)
  return unless is_admin?
  content_for(:admin_panel) { yield }
  render :partial => 'admin/panel', :locals => { :url_params => url_params.merge(:toggle_admin => true) }
end

# in views/admin/_panel.rhtml
<div class="admin-div" id="forum-admin">
  <%= yield :admin_panel if session[:show_admin_panel] %>
  <p><%= gen_remote_link("[Admin]", "title", url_params) %></p>
</div>


Here the content_for method just puts the result of the block in an instance variable. Calling "yield :admin_panel" in the view ends up calling this instance variable.

Railscasts - Free Ruby on Rails Screencasts

Re: render_to_string from a helper

Slick!  Thanks!