Render view outside of a controller/view

We're putting together a searchable online help site in Rails, and I'm using FastRI to full-text index some existing Rails view pages to do it.

I don't want to use Sphinx or some other solution, just because it's way overkill for the 15-20 .html.erb partials I am looking to index.

So... I need to violate MVC (flog me now)... trying to write a Ruby class that renders each of these pages as a string, and feeds the rendered text as a string into FastRI for indexing.

I've seen plenty of links, most of which have about the same approach.

Here are a few of the approaches I have seen:

http://railsforum.com/viewtopic.php?id=16885 http://www.compulsivoco.com/2008/10/rendering-rails-partials-in-a-model-or-background-task/ http://ethilien.net/archives/render-rails-templates-anywhere-even-in-a-model/ http://www.swombat.com/rails-rendering-templates-outside-of-a-contro

All seem to work until I try to render a page with a link_to helper with a restful route (games_path) or a hash representing the path.

As best I can tell, I think I need a controller and some kind of mock request.

Using a slight derivation from the more recent blog post:

def render_anywhere(partial, assigns={})   view = ActionView::Base.new(Rails::Configuration.new.view_path, assigns)   ActionView::Base.included_modules.each { |helper| view.extend helper }   view.extend ApplicationHelper   view.render(partial) end

render_anywhere(:file => "/full/path/to/app/views/helps/ _send_credentials.html.erb")

gives me this:

ActionView::TemplateError (You have a nil object when you didn't expect it! The error occurred while evaluating nil.url_for) on line #6 of app/ views/helps/_send_credentials.html.erb:

actionpack (2.3.4) lib/action_view/helpers/url_helper.rb:85:in `send' actionpack (2.3.4) lib/action_view/helpers/url_helper.rb:85:in `url_for' app/views/helps/_send_credentials.html.erb:6

As best I can tell, I'm missing a controller and possibly a request object in there somewhere, since it seems to be barfing on:

@controller.send(:url_for, options)

I've tried assigning a mock controller in there, and then it looks like the controller needs a request object. I've tried following the rabbit down the hole, and I just keep getting "nil object when you didn't expect it" errors of various flavors.

I'm lost, any ideas?

Thanks in advance--

Jeff Wigal

jeff_wigal wrote:

We're putting together a searchable online help site in Rails, and I'm using FastRI to full-text index some existing Rails view pages to do it.

I don't want to use Sphinx or some other solution, just because it's way overkill for the 15-20 .html.erb partials I am looking to index.

So... I need to violate MVC (flog me now)... trying to write a Ruby class that renders each of these pages as a string, and feeds the rendered text as a string into FastRI for indexing.

[...]

If you need to do this, then you probably have a design problem which you should correct. It seems to me that you'd only be using ERb for your help text for one of two reasons:

1. The help text is written right in the ERb file, and Ruby is only used for things like navbars which aren't part of the help text itself.

2. The help text is not written in the ERb file, but rather comes from a database or something.

In neither case do you need to render the ERb to index the information. For case 1, just index the literal ERb (perhaps with all <%...%> removed for security). For case 2, use a database query.

If neither of these is your use case, please explain further.

Best,

Thanks for your response.

I'm basically building static content within an existing rails application.

I can just as easily do this with straight HTML, but since this is a static site for our online documentation. I'm using ERb because the individual help documents may link to each other, or link to other parts of the rails application. I'd prefer to use the link_to helper methods that tie into the routes, so that if a route changes, I'm not having to hunt down these changes in my static content.

Our help text is written into the ERb files, and Ruby is used for some of the basic helper methods (link_to, maybe some of the other more common ones but nothing too exotic). We'd rather not put the help text into the database, since it's a lot easier to maintain by editing the help files while we're editing the application code.

We have direct control over the help text, so arbitrary code execution is not a concern.

My show.html.erb action looks something like this:

<div id="help">   <h1><%= h @page.description%></h1>   <%= render @page.file %> </div> <div class="clear"></div>

@page.file returns a string representing the partial to be rendered, and we have about 15-25 ERb partials that represent our help text.

Thanks--

Quoting jeff_wigal <jeff@referee-assistant.com>:

We're putting together a searchable online help site in Rails, and I'm using FastRI to full-text index some existing Rails view pages to do it.

As you have found, trying to fake a controller, request, etc. is a lot of work. One possibilty is to just throw the necessary methods into some applicable controller and call them as normal GET request:

def render_anywhere   return unless local_request?   # DB access and/or instance variable assignment...   page = render_to_string :action => ... OR :template => ...   FastRI.whatever(page) end

This only is safe if there are no untrusted users on the server host.

HTH,   Jeffrey

Assuming I understand you, what *I* do whenever I want to use Rails' routing helpers in a non-Controller/View is just "include ActionController::UrlWriter" in the class. link_to is in ActionView::UrlHelper... you get the idea.

Thanks for the advice... I think I am going to pull this out of the model and make it a controller method (/help/reindex) since that will give me easier access to the stuff I need.