# See Bloggit::Template
require 'test/unit/assertions'
require 'erb'
module Bloggit
# = Template
class Template
include Test::Unit::Assertions
attr_reader :file_path, :settings, :template, :template_src, :yaml_parts, :content_parts
def initialize(file_path, content=nil)
@file_path = file_path
@settings = {}
@template_src = ""
@content_parts = {}
unless file_path.nil?
@yaml_parts = YAML::load_stream( File.open(file_path, 'r') )
@yaml_parts.documents.each do |yml|
if yml.is_a? Hash
@settings.merge!(yml)
elsif yml.is_a? String
@template_src << yml.to_s
else
puts "TEMPLATE: Uh, not sure what to do with this part"
end
end
else
@template_src = content
end
@template = ERB.new(@template_src, 0, "%<>")
end
def render(params={})
if name = params.fetch(:snippet, nil)
Template.generate "snippets/#{name}", params
elsif ctx = params.fetch(:template, nil)
@params = ctx
if layout_name = @settings.fetch('layout', nil)
output = @template.result( binding )
ctx['content_for_layout'] = output
Template.generate "layouts/#{layout_name}", ctx
else
@template.result( binding )
end
elsif name = params.fetch(:plugin, nil)
puts "RENDER PLUGIN #{name}"
elsif name = params.fetch(:content_for, nil)
Template[name].to_s
elsif obj = params.fetch(:content, nil)
obj.render_content(binding)
elsif content = params.fetch(:text, nil)
Template.from_text(content).template.result(binding)
else
puts "Cannot find renderer for #{params.to_yaml}"
end
end
def url_for(params={})
if post = params.delete(:post)
if post.is_a? String
Template.path_to_root + site.get_post_by_slug(post).permalink
elsif post.is_a? Post
Template.path_to_root + post.permalink
end
elsif page = params.delete(:page)
if page.is_a? String
Template.path_to_root + site.get_page_by_slug(page).permalink
elsif page.is_a? Page
Template.path_to_root + page.permalink
end
elsif tag = params.delete(:tag)
if tag.is_a? String
Template.path_to_root + Tag.tag_list[tag].permalink
elsif tag.is_a? Tag
Template.path_to_root + tag.permalink
end
elsif media = params.delete(:media)
if media.is_a? String
Template.path_to_root + site.media[media].permalink
elsif media.is_a? Media
Template.path_to_root + media.permalink
end
elsif script = params.delete(:script)
script += '.js' unless script.ends_with? '.js'
"#{ Template.path_to_root }theme/scripts/#{ script }"
elsif style = params.delete(:style)
style += '.css' unless style.ends_with? '.css'
"#{ Template.path_to_root }theme/styles/#{ style }"
elsif image = params.delete(:image)
"#{ Template.path_to_root }theme/images/#{ image }"
elsif path = params.delete(:from_root)
Template.path_to_root + path
elsif url = params.delete(:url)
url
end
end
def tag_links(obj)
links = []
obj.tags.each do |tag|
links << link_to(tag, :tag=>tag)
end
links
end
def url_for_blog
"#{ Template.path_to_root }#{site.build_post_path('index.html')}"
end
def url_for_home
"#{ Template.path_to_root }index.html"
end
def url_for_feed
"#{ Template.path_to_root }#{site.settings.syndication.filename}"
end
def link_to(title, params={})
url = url_for(params)
title ||= url
atts = ""
params.each {|k,v| atts << %Q|#{k}="#{v.to_s}"|}
%Q|#{title}|
end
def link_to_home(title, params={})
atts = ""
params.each {|k,v| atts << %Q|#{k}="#{v.to_s}"|}
%Q|#{title}|
end
def link_to_blog(title, params={})
atts = ""
params.each {|k,v| atts << %Q|#{k}="#{v.to_s}"|}
%Q|#{title}|
end
def link_to_feed(title, params={})
atts = ""
params.each {|k,v| atts << %Q|#{k}="#{v.to_s}"|}
%Q|#{title}|
end
def media_link_to(media_path, params={})
media_item_url = url_for(:media=>media_path)
size = params.delete(:size).to_s
atts = %Q|border="0" |
atts << %Q|width="#{size}" | if size.ends_with?('%')
image_tag = %Q|
|
link_to image_tag, params
end
# def link_to_archive(title, params={})
# atts = ""
# params.each {|k,v| atts << %Q|#{k}="#{v.to_s}"|}
# %Q|#{title}|
# end
def site
Template.site
end
def [](key)
if @params.has_key? key.to_s
@params[key.to_s]
elsif @params.has_key? key.to_sym
@params[key.to_sym]
else
super(*args)
end
end
def method_missing(name, *args)
if @params.has_key? name.to_s
@params[name.to_s]
elsif @params.has_key? name.to_sym
@params[name.to_sym]
elsif Template.tag_registered?(name)
Template.execute_tag(name, self, *args)
else
raise "Template Error: No method/data found for '#{name}' in template #{file_path}"
end
end
def capture(*args, &block)
# execute the block
begin
buffer = eval('_erbout', block.binding)
rescue
buffer = nil
end
if buffer.nil?
capture_block(*args, &block).to_s
else
capture_erb_with_buffer(buffer, *args, &block).to_s
end
end
def content_for(name, content = nil, &block)
Template[name] = capture(&block)
end
def render_binding
binding
end
private
def capture_block(*args, &block)
block.call(*args)
end
def capture_erb(*args, &block)
buffer = eval('_erbout', block.binding)
capture_erb_with_buffer(buffer, *args, &block)
end
def capture_erb_with_buffer(buffer, *args, &block)
pos = buffer.length
block.call(*args)
# extract the block
data = buffer[pos..-1]
# replace it in the original with empty string
buffer[pos..-1] = ''
data
end
def erb_content_for(name, &block)
Template[name] = capture_erb(&block)
end
def block_content_for(name, &block)
Template[name] = capture_block(&block)
end
class << self
private :new
attr_accessor :site, :template_dir, :content_parts, :src_path, :current_template, :folder_depth
include Test::Unit::Assertions
def config
yield self
assert !@site.nil?, 'Site cannot be nil'
@template_dir = File.join(@site.base_path, 'themes', @site.theme, 'templates') if @template_dir.nil?
@is_ready = true
@template_cache = {}
@content_parts = {}
end
# Do not call directly!
def generate(template, ctx) #:nodoc:
assert @is_ready, "Template isn't ready"
@template_cache[template] ||= from_file( "#{template_dir}/#{template.to_s}.rhtml" )
@template_cache[template].render(:template=>ctx).to_s
end
def render(template, src_path, ctx)
@content_parts.clear
@folder_depth = (src_path == '.') ? 0 : src_path.split(File::SEPARATOR).length
generate(template, ctx)
end
def set_content_for(key, value)
@content_parts[key] = [] unless @content_parts.has_key? key
@content_parts[key] << value
end
def [](key)
if @content_parts.has_key?( key )
@content_parts[key].join
else
""
end
end
def []=(key,value)
set_content_for(key, value)
end
def path_to_root
'../' * @folder_depth
end
def from_file(path)
path = File.expand_path(path)
raise "Template file must exist" unless File.exists?( path )
new(path)
end
def from_text(text)
new(nil, text)
end
# Custom Tag support
def register_tag(name, &block)
@custom_tags ||= {}
@custom_tags[name.to_sym] = block
end
def tag_registered?(name)
@custom_tags.has_key? name.to_sym
end
def execute_tag(name, template, *args)
@current_template = template
@custom_tags[name.to_sym].call( *args )
end
end
end
end