1

In Rails 4, how does one execute an arbitrary controller action and render the response to a string?

This is obviously a bad practice, but there are circumstances when it becomes very difficult to avoid:

  • You are making an offline copy or e-mail attachment of a dynamically rendered pdf (or any self-contained response).
  • Aforementioned response involves views and controllers not under your control, or in external gems.
  • Aforementioned views involve layouts and dozens of partials using relative paths and custom template rendering engines.

In some circumstances (when calling from another controller), it is possible to eliminate the dependency on the controller by replacing any data needed by the view. However, this typically still breaks the view rendering, as relative paths can no longer be passed to the render function within partials (among other issues).

2
  • What's the context in which you're rendering this arbitrary action? Within another action? From the console? Rake task? It's relatively common from within the same controller if you want the same output (e.g. render new if create fails). Commented Oct 6, 2014 at 13:49
  • Both from an ActionMailer controller, and from integration tests. Commented Oct 6, 2014 at 15:50

1 Answer 1

0

So I haven't actually done this. Or anything like it.

But I remembered you can get a Rack app object for any arbitrary rails action. So it seemed like you could use that rack-compat interface to all an arbitrary action and get a response internally, without actually having to make an http request.

Googled around things I vaguely remembered to put the pieces together, and got this, which I have tried out in a very simple dummy app I made, and it seems to work:

rack_env = Rack::MockRequest.env_for("/some/url?foo=bar")
rack_app = SomeController.action(:index)

(status, headers, rack_body) = rack_app.call(rack_env)
str = rack_body.body

I was surprised to need to call #body on the thing I already thought was the body, not sure exactly what's going on I guess I don't entirely understand the rack api. Not sure if MockRequest is the right way to build a request. Not sure if there's a better way (but heck 3-4 lines ain't bad). But it does seem to work.

(There's probably a way to get the 'right' way to work too, with enough work -- there are for instance ways to change or add to the view template lookup paths, to put the original controller's views on the view lookup path, even when you're rendering the template from a new view. But I believe you that it gets painful, and am honestly not sure in this case what the 'right' way to do it is, I think the Rack method seems reasonable)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.