Rails >= 3.0.8 render inline code with layout not working - ruby-on-rails

It seems the render method has changed.
In the view I used to be able to do the following:
= render :layout => 'some_layout' do
some stuff to be rendered
It seems the best fix is to move the content into a partial and call the layout
= render :partial => 'some stuff to be rendered', :layout => 'some_layout'
I was just wondering if anyone had come across this and if it is a bug or an intended change?
EDIT
Rendering a block inline with a layout works. Check out the part about applying a layout to a block within any template at http://api.rubyonrails.org/classes/ActionView/Partials.html
The issue I am having is with the latest version of HAML not rendering nested render calls properly.
https://github.com/nex3/haml/issues/412

From your post, it appears you are trying to do this in a view.
Is it possible that you are confusing ActionController's render and ActionView's render? Looking # the API documentation for 2.3.8 & 3.x, it doesn't seem there was ever a :layout option within ActionView's render.
UPDATE
Actually, I may have been off-base. It does seem that there is an :inline option as described here.
render(options = {}, locals = {}, &block)
Returns the result of a
render that’s dictated by the options hash. The primary options are:
:partial - See ActionView::Partials.
:update - Calls update_page with the block given.
:file - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
:inline - Renders an inline template similar to how it’s done in the controller.
:text - Renders the text passed in out.

This is fixed in the most recent version of HAML v3.1.3

Related

Rails 3 vs 2.3.5 render oddities with :partial and :layout

I've come across an oddity that I can't quite explain with regards to Rails 3 and rendering partials with layouts (from the controller). I'm hoping someone can provide a little insight into what's happening.
First off, we'll call this controller a "legacy" controller. It's been around for a long time and is doing a lot of things wrong, but I'm not looking to refactor it at this point so I'm trying to find ways to work with what we have.
The new action is something like this (in the BarsController)
def new
if something
render :partial => "foo", :layout => "bars"
elsif something_else
render :partial => "foo2", :layout => "bars"
elsif something_else_else
render :partial => "foo3", :layout => "bars"
else
render :partial => "foo4", :layout => "bars"
end
Now, in Rails 2.3.5, this worked fine. It would render the appropriate partial inside the appropriate layout -- I realize the layout option is redundant here as it would default to the bars layout regardless. When we upgraded to Rails 3.0.x, we started getting errors as follows:
Missing partial layouts/bars with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html]
Clearly the layouts/bars.html.erb file is and always has been there, so I couldn't figure it out. I was able to render with :layout => false, but that of course wasn't going to work. Eventually I figured out that if I do either of the following, it works:
1) Rename my layout to _bars.html.erb instead of bars.html.erb and:
render :partial => 'foo2', :layout => 'bars'
2) Keep my layout as bars.html.erb (what I want) and :
render '_foo2' # :partial option is redundant here anyway
It seems as though by using the :partial option instead of the string as first parameter is causing rails to apply the _name.html.erb convention to both the partial AND the layout. If I put in the underscore on my own, it falls back to the behaviour I expected which is to not prepend an _ infront of the name of the layout.
Does anyone know why this is the case?
EDIT Alright, not sure how I missed this... but here's something in the docs making mention of this. It seems as though it's been around since 2.3.8, perhaps it was done differently in 2.3.5 (what we were running on)?
3.4.3 Partial Layouts
A partial can use its own layout file, just as a view can use a
layout. For example, you might call a partial like this:
<%= render "link_area", :layout => "graybar" %> This would look for a
partial named _link_area.html.erb and render it using the layout
_graybar.html.erb. Note that layouts for partials follow the same
leading-underscore naming as regular partials, and are placed in the
same folder with the partial that they belong to (not in the master
layouts folder).
Here's my own answer to the question based on what I've edited above:
Since Rails 2.3.8, it would appear as though the default behaviour when rendering a partial with render :partial => 'foo', :layout => 'bars' is to expect a "partial layout" file as well as a partial view file. In this case it will expect
app/views/_foo.html.erb as well as app/views/layouts/_bars.html.erb
For anyone encountering this problem upgrading from Rails 2.3.5, here's the solution I found to have the least amount of impact:
render '_foo', :layout => 'bars'
This solution does not assume you're rendering a partial and therefore does not expect a partial layout. The other option would be to duplicate your layout to
app/views/layouts/_bars.html.erb
and using
render :partial => 'foo', :layouts => 'bars'
but that results in some duplication of code.
RAILS 2.3.8+ DOC REGARDING THIS:
3.4.3 Partial Layouts
A partial can use its own layout file, just as a view can use a
layout. For example, you might call a partial like this:
<%= render "link_area", :layout => "graybar" %> This would look for a
partial named _link_area.html.erb and render it using the layout
_graybar.html.erb. Note that layouts for partials follow the same
leading-underscore naming as regular partials, and are placed in the
same folder with the partial that they belong to (not in the master
layouts folder).

How can I setup a user-defined template in prawn - prawnto?

I'm using prawn with prawnto in a rails 3 application to generate some pdf's. I have created a pdf.prawn template for a specific controller's action, and it is working fine.
I have the following code in my 'document' controller, as suggested by prawnto documentation:
respond_with(#document) do |format|
format.html
format.pdf { render :layout => false } # Add this line
end
What I would like to achieve is to allow each user of the app to select different templates, and accordingly, be able to render the corresponding XXXX.pdf,prawn templates.
How can I define dynamically the prawn template name to be rendered, based on a variable, instead of the default show.pdf.prawn template?
I have been looking for a couple of days for this feature, but have
not found anything similar.
I am willing to drop prawnto and use plain prawn if this could solve this issue.
Thank you very much for your time!
Alex
I guess you can use,
render :template => "path/to/xxx.pdf.prawn", :layout => false

Rails: How do you render with a layout inside a JS template?

I'm familiar with using Ajax templates to update particular parts of a page, but how do you render with layout when doing so? For example, given a layout:
#foo
= yield :foo
a simple view "show.html.haml":
= render #bar
and a partial:
- content_for :foo
= bar.to_html
... the HTML result would render within the layout and I'd see my bar content, but say I want to use Ajax to update only the #foo div. I create "show.js.erb":
$("#foo").html("<% escape_javascript(render(#bar)) %>");
But the result is nothing, as my _bar partial is rendered but outside of the layout, thus my :foo content is never yielded to. How do I get the JS template to render inside that layout?
I've found two answers, but I wonder if there's a better way still. These answers are for Rails 3, by the way.
1. Use a separate JS layout.
This is probably the "more correct" way, but unfortunately didn't work for my situation as I'll explain shortly.
What I wasn't fully aware of, is that in a JS request, the lookup formats are set to JS and HTML. Meaning that the controller will render the HTML template if the JS template does not exist.
But it will not look to the HTML layout in the same fashion, meaning the HTML template will be rendered, but the content_for block is never yielded to, leading to an empty response.
So to make the simple example above work out-of-the-box. You'd delete "show.js.erb" and add a JS layout, (e.g. "bars.js.erb") in the lookup path, which would look like this:
$("#foo").html("<% escape_javascript(yield(:foo)) %>");
In this way, the HTML template is rendered, but in the JS layout, and the HTML of #foo is swapped out for the new content of the response.
2. Render the HTML content in the JS response block.
However, #1 this was not an ideal answer for me. My app uses many nested layouts, most of which are very similar. To make the above example work I'd have to create a lot of JS layouts, all of which more or less copies of the original HTML layouts. A waste of time, and not at all DRY. So I came up with this solution.
It feels less ideal than #1, and please tell me if there's a more appropriate way. But this is what I came up with:
# in bars_controller.rb
def show
# ...
respond_to do |format|
format.js do
lookup_context.update_details(:formats => [:html]) do
#content = render_to_string
end
render
end
end
end
In this way I temporarily set the mimetype for the template lookup to be HTML, render the content to a variable, then render the JS template:
// show.js.erb
$("#foo").html("<%= escape_javascript(#content) %>");
There is one further complication to this. In my nested layout setup, in the HTML response, the layout calls the rendering of its parent to continue to build the body, leading to the complete page. In my case, I want it to simply return the body content. So while I don't need JS layouts for this solution, I do need to slightly change my layout, like this:
-# my_layout.html.haml
-# (given a parent layout that yields to :body)
- content_for :body do
= yield(:foo)
- if request.xhr?
= yield(:body)
- else
= render :file => "layouts/my_parent_layout"
In this way the parent is not called on a JS request, simply resulting in the body (up to this point in the nested layout stack).

How to use partial in views with different alias MIME?

Im using 2 different sets of views for 2 different user's roles.
Im using register_alias :
Mime::Type.register_alias "text/html", :basic
in the controller:
class SomeController < ApplicationController
def index
# …
respond_to do |format|
format.html # index.html.erb (advance)
format.basic # index.basic.erb
end
end
end
In some case I have to use the same code in both views, then I would use a Partial, but because of the MIME alias, I have to use 2 identical partials:
my_partial.html.erb and my_partial.basic.erb
I think there is a solution to DRY the code and use only a partial.
Do you have some solutions ?
thank you,
Alessandro
Old Answer:
I probably tried 50 different things until I figured out the right way of writing the partial once, but it was worth it because it's super simple:
Inside your index view, you normally do:
<%= render "my_partial" %>
This implicitly gets mapped to the partial corresponding to the Mime you requested, so it implies having two partial implementations. If you want a DRY partial, simply explicitly specify the format:
<%= render "my_partial.html" %>
As an added bonus of this observation, if your responds_to block of code is really just to switch based on the format and has no logic inside it, you can entirely remove that block of code and things still work implicitly.
Rails 3.2 update:
Rails has deprecated support for the above and support has been completely removed in the latest version of Rails. The following is the correct way as of Rails 3.2:
<%= render :partial => "my_partial", :formats => [:html] %>

How can you render a template within a layout using Liquid template language?

I'm trying to render a liquid template within a liquid layout (Liquid Template lang, not CSS liquid layout stuff). I can't seem to get the layout part to render. Currently using:
assigns = {'page_name' => 'test'}
#layout = Liquid::Template.parse(File.new(#theme.layout.path).read)
#template = Liquid::Template.parse(File.new(self.template.path).read)
#rend_temp = #template.render(assigns)
#rend_layout = #layout.render({'content_for_layout' => #rend_temp})
render :text => #rend_layout, :content_type => :html
The resulting HTML of the page shows that the 'template' rendered in liquid fine, but it isn't wrapped with the layout (replacing 'content_for_layout' in the layout with the rendered template)
Just to let anyone else know who comes across this problem, the code posted above actually does work, the issue is with the variable named #template. I renamed #template, and #layout to #_tempalte, and #_layout and everything works as expected.
For using liquid in ruby on rails (especially rails 3) - I believe the proper way to render your liquid templates (and also maintain all the work rails is doing for you) is as follows...
The liquid gem itself provides a liquid_view for rails so you can wire up the rails to look for "liquid" templates when you call #render. This liquid_view only works fully with rails 2.3
but can easily be updated to work with rails 3 by making the following update
if content_for_layout = #view.instance_variable_get("#content_for_layout")
assigns['content_for_layout'] = content_for_layout
elsif #view.content_for?(:layout)
assigns["content_for_layout"] = #view.content_for(:layout)
end
assigns.merge!(local_assigns.stringify_keys)
This can be seen here --> https://github.com/danshultz/liquid/commit/e27b5fcd174f4b3916a73b9866e44ac0a012b182
Then to properly render your liquid view just call
render :template => "index", :layout => "my_layout", :locals => { liquid_drop1 => drop, liquid_drop2 => drop }
In our application, since we have a handful of common liquid attributes we have overriden the "render" method in our base controller to automatically include the default locals by referencing #liquid_view_assigns which roll up additionally added liquid drops for the render call
def render(...)
options[:locals] = options.fetch(:locals, {}).merge(liquid_view_assigns)
super
end

Resources