module Padrino
module Helpers
###
# Helpers related to buffer output for various template engines.
#
module OutputHelpers
def self.included(base) # @private
base.send(:include, SinatraCurrentEngine) unless base.method_defined?(:current_engine)
end
##
# Module used to detect the current engine in vanilla sinatra apps.
# @private
module SinatraCurrentEngine
attr_reader :current_engine
def render(engine, *) # @private
@current_engine, engine_was = engine, @current_engine
output = super
@current_engine = engine_was
output
end
end
##
# Captures the html from a block of template code for any available handler.
#
# @param [Object] *args
# Objects yield to the captured block
# @param [Proc] &block
# Template code to capture as html
#
# @return [String] Captured html resulting from the block
#
# @example
# capture_html(&block) => "...html..."
# capture_html(object_for_block, &block) => "...html..."
#
# @api semipublic
def capture_html(*args, &block)
handler = find_proper_handler
captured_html = ""
if handler && handler.is_type? && handler.block_is_type?(block)
captured_html = handler.capture_from_template(*args, &block)
end
# invoking the block directly if there was no template
captured_html = block_given? && block.call(*args) if captured_html.blank?
captured_html
end
alias :capture :capture_html
##
# Outputs the given text to the templates buffer directly.
#
# @param [String] text
# Text to concatenate to the buffer.
#
# @example
# concat_content("This will be output to the template buffer")
#
# @api semipublic
def concat_content(text="")
handler = find_proper_handler
if handler && handler.is_type?
handler.concat_to_template(text)
else # theres no template to concat, return the text directly
text
end
end
alias :concat :concat_content
##
# Returns true if the block is from a supported template type; false otherwise.
# Used to determine if html should be returned or concatenated to the view.
#
# @param [Block] block
# Determine if this block is a view template.
#
# @example
# block_is_template?(block) => true
#
# @return [Boolean] True if the block is a template; false otherwise.
#
# @api semipublic
def block_is_template?(block)
handler = find_proper_handler
block && handler && handler.block_is_type?(block)
end
##
# Capture a block or text of content to be rendered at a later time.
# Your blocks can also receive values, which are passed to them by yield_content
#
# @overload content_for(key, content)
# @param [Symbol] key Name of your key for the content yield.
# @param [String] content Text to be stored for this key.
# @overload content_for(key, &block)
# @param [Symbol] key Name of your key for the content yield.
# @param [Proc] block Block to be stored as content for this key.
#
# @example
# content_for(:name) { ...content... }
# content_for(:name) { |name| ...content... }
# content_for(:name, "I'm Jeff")
#
# @api public
def content_for(key, content = nil, &block)
content_blocks[key.to_sym] << (block_given? ? block : Proc.new { content })
end
##
# Is there a content block for a given key?
#
# @param [Symbol] key
# Name of content to yield
#
# @return [TrueClass,FalseClass] Result html for the given +key+
#
# @example
# content_for? :header => true
#
# @api public
def content_for?(key)
content_blocks[key.to_sym].present?
end
##
# Render the captured content blocks for a given key.
# You can also pass values to the content blocks by passing them
# as arguments after the key.
#
# @param [Symbol] key
# Name of content to yield
# @param *args
# Values to pass to the content block
#
# @return [String] Result html for the given +key+
#
# @example
# yield_content :include
# yield_content :head, "param1", "param2"
# yield_content(:title) || "My page title"
#
# @api public
def yield_content(key, *args)
blocks = content_blocks[key.to_sym]
return nil if blocks.empty?
blocks.map { |content| capture_html(*args, &content) }.join
end
protected
##
# Retrieves content_blocks stored by content_for or within yield_content
#
# @example
# content_blocks[:name] => ['...', '...']
#
def content_blocks
@content_blocks ||= Hash.new { |h,k| h[k] = [] }
end
##
# Retrieves the template handler for the given output context.
# Can handle any output related to capturing or concating in a given template.
#
# @example
# find_proper_handler =>
#
def find_proper_handler
OutputHelpers.handlers.map { |h| h.new(self) }.find { |h| h.engines.include?(current_engine) && h.is_type? }
end
end # OutputHelpers
end # Helpers
end # Padrino