I have revived this post in order to get comments on my implementation
of nested layouts. I created custom tags to allow for this. Here is my
solution to the scenario given above.
First, in “/”, create a page part called “layout” to be used as the
nested layout. It will look like this:
<r:layout_part name=“left”>
<r:layout_part name=“main”>
Then edit the “body” page part of “page1” and “page2” to look like this:
<r:layout />
The code behind r:layout and r:layout_part follows:
# the layout is actually a page part
# if the named page part is not a part of the current page
# ancestors are searched until the named page part is found
tag 'layout' do |tag|
ret_val = ""
layout_name = tag.attr['layout_name'] || "layout"
page = tag.locals.page
page_part_names =
page.instance_variable_get(:@parts).collect(&:name)
parts_for_layout = tag.attr[‘parts’] ||
page_part_names.delete_if{|part| part == “body” || part == layout_name }
found = nil
og_page_id = page.id
if !page_part_names.include? layout_name
page.ancestors.each do |ancestor|
pp =
PagePart.find_all_by_page_id(ancestor.instance_variable_get(:@attributes)[“id”],
:include => [:page])
ancestor_part_names = pp.collect(&:name)
if ancestor_part_names.detect{|part| part == layout_name
} && !found
tag.locals.page = pp.detect{|p| p.name ==
layout_name}.page
found = 1
end
end
tag.render(‘content’, {“part” => layout_name,
“parts_page_id” => og_page_id})
else
tag.render(‘content’, {“part” => layout_name})
end
end
tag 'layout_part' do |tag|
part_to_render = tag.attr['name']
# this mess is necessary; trying to get tag_binding_stack
directly from the context returns a different array
page = tag.locals.page
response = page.instance_variable_get(:@response)
template = response.instance_variable_get(:@template)
controller = template.instance_variable_get(:@controller)
pagee = controller.instance_variable_get(:@page)
context = pagee.instance_variable_get(:@context)
tbs = context.instance_variable_get(:@tag_binding_stack)
parts_page_id = tbs.last.attributes["parts_page_id"]
# if the layout is in the same page as the parts being laid out
# the parts_page_id will not have been specified, so do not
reset the tag page
unless parts_page_id.blank?
parts_page = Page.find(parts_page_id)
tag.locals.page = parts_page
end
# e.g. name of part is specified in the tag as 'blah'
# but the page calling the layout tag which wraps this tag does
not have a ‘blah’ part
# in such a case don’t display this tag
# NOTE: there is likely a more efficient way to do this check
than a db query
pp = PagePart.find_by_name_and_page_id(part_to_render,
parts_page_id || tag.locals.page.id)
tag.expand unless pp.blank?
end
Thank you in advance for any criticism/comments.