# 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