# encoding: utf-8
module Nanoc::Helpers
# Provides functionality for rendering layouts as partials.
module Rendering
include Nanoc::Helpers::Capturing
# Renders the given layout. The given layout will be run through the first
# matching layout rule.
#
# When this method is invoked _without_ a block, the return value will be
# the rendered layout (a string) and `_erbout` will not be modified.
#
# When this method is invoked _with_ a block, an empty string will be
# returned and the rendered content will be appended to `_erbout`. In this
# case, the content of the block will be captured (using the
# {Nanoc::Helpers::Capturing} helper) and this content will be made
# available with `yield`. In other words, a `yield` inside the partial
# will output the content of the block passed to the method.
#
# (For the curious: the reason why {#render} with a block has this
# behaviour of returning an empty string and modifying `_erbout` is
# because ERB does not support combining the `<%= ... %>` form with a
# method call that takes a block.)
#
# The assigns (`@item`, `@config`, …) will be available in the partial. It
# is also possible to pass custom assigns to the method; these assigns
# will be made available as instance variables inside the partial.
#
# @param [String] identifier The identifier of the layout that should be
# rendered
#
# @param [Hash] other_assigns A hash containing extra assigns that will be
# made available as instance variables in the partial
#
# @example Rendering a head and a foot partial around some text
#
# <%= render 'head' %> - MIDDLE - <%= render 'foot' %>
# # => "HEAD - MIDDLE - FOOT"
#
# @example Rendering a head partial with a custom title
#
# # The 'head' layout
#
<%= @title %>
#
# # The item/layout where the partial is rendered
# <%= render 'head', :title => 'Foo' %>
# # => "Foo
"
#
# @example Yielding inside a partial
#
# # The 'box' partial
#
# <%= yield %>
#
#
# # The item/layout where the partial is rendered
# <% render 'box' do %>
# I'm boxy! Luvz!
# <% end %>
#
# # Result
#
# I'm boxy! Luvz!
#
#
# @raise [Nanoc::Int::Errors::UnknownLayout] if the given layout does not
# exist
#
# @raise [Nanoc::Int::Errors::CannotDetermineFilter] if there is no layout
# rule for the given layout
#
# @raise [Nanoc::Int::Errors::UnknownFilter] if the layout rule for the given
# layout specifies an unknown filter
#
# @return [String, nil] The rendered partial, or nil if this method was
# invoked with a block
def render(identifier, other_assigns = {}, &block)
# Find layout
layout = @layouts[identifier]
layout ||= @layouts[identifier.__nanoc_cleaned_identifier]
raise Nanoc::Int::Errors::UnknownLayout.new(identifier) if layout.nil?
# Visit
Nanoc::Int::NotificationCenter.post(:visit_started, layout)
Nanoc::Int::NotificationCenter.post(:visit_ended, layout)
# Capture content, if any
captured_content = block_given? ? capture(&block) : nil
# Get assigns
assigns = {
content: captured_content,
item: @item,
item_rep: @item_rep,
items: @items,
layout: Nanoc::LayoutView.new(layout),
layouts: @layouts,
config: @config,
site: @site
}.merge(other_assigns)
# Get filter name
filter_name, filter_args = @site.unwrap.compiler.rules_collection.filter_for_layout(layout)
raise Nanoc::Int::Errors::CannotDetermineFilter.new(layout.identifier) if filter_name.nil?
# Get filter class
filter_class = Nanoc::Filter.named(filter_name)
raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if filter_class.nil?
# Create filter
filter = filter_class.new(assigns)
begin
# Notify start
Nanoc::Int::NotificationCenter.post(:processing_started, layout)
# Layout
result = filter.setup_and_run(layout.raw_content, filter_args)
# Append to erbout if we have a block
if block_given?
# Append result and return nothing
erbout = eval('_erbout', block.binding)
erbout << result
''
else
# Return result
result
end
ensure
# Notify end
Nanoc::Int::NotificationCenter.post(:processing_ended, layout)
end
end
end
end