# HTML::Template extension(?) # # Author:: Kazuhiro Yoshida (mailto:moriq@moriq.com) # Copyright:: Copyright (c) 2003 moriq # License:: Distributes under the same terms as Ruby require 'html/template' require 'cgi' module HTML class Template def initialize(opt={}) @param = {} @interp = Interp.new filename, tmpl = @interp.load_file(opt) @interp.parse(tmpl) end def output @interp.interpret(@param) end class Interp def load_file(opt) if opt['filename'] if opt['path'] @parser.load_file(opt['filename'], opt['path']) else @parser.load_file(opt['filename']) end else [nil, ''] end end def initialize @tokens = nil @parser = HTML::Template::Parser.new end def _eval(env, idx, cond = true, blocklev = @block.size) ret = @ret while true break if idx >= @tokens.size act, sub = @tokens[idx] idx+=1 in_block = blocklev == @block.size catch(:action){ case act when :TEXT cond and @ret.push sub next when :CLOSE last_act = @block.pop throw :action unless in_block unless last_act == sub raise "#{act}: #{sub} is not #{last_act || 'opened'}" end return idx when :TMPL_ELSE throw :action unless in_block last_act = @block.last case last_act when :TMPL_IF when :TMPL_UNLESS else raise "#{act}: not in condition block (#{last_act})" end cond = ! cond when :TMPL_INCLUDE throw :action unless cond # @ret.push ... when :TMPL_VAR throw :action unless cond v = env[sub['name']] case sub['escape'] when "HTML" v = CGI::escapeHTML(v) when "URL" v = CGI::escape(v) end ret.push v when :TMPL_IF @block.push act throw :action unless cond condition = env[sub['name']] idx = _eval(env, idx, condition) when :TMPL_UNLESS @block.push act throw :action unless cond condition = ! env[sub['name']] idx = _eval(env, idx, condition) when :TMPL_LOOP unless cond @block.push act throw :action end ary = env[sub['name']] unless ary.is_a? Array raise "#{act}: env[#{sub['name']}] '#{ary}' is not Array" end loop_limit = ary.size - 1 loop_range = (0..loop_limit) dmy = nil ary.each_with_index do |e,i| unless e.is_a? Hash raise "#{act}: env[#{sub['name']}] item '#{e}' is not Hash" end nblock = @block.size @block.push act env.each do |key, value| e[key] = value unless e.key?(key) end e["__FIRST__"] = i == 0 e["__LAST__"] = i == loop_limit e["__INNER__"] = loop_range.include?(i) e["__ODD__"] = i%2 == 0 dmy = _eval(e, idx) unless @block.size == nblock raise "EOF: not closed block (#{@block.join(', ')})" end end if dmy idx = dmy else @block.push act idx = _eval(env, idx, false) end end } end idx end def tokenize(*args) @parser.tokenize(*args) end def parse(src) @tokens = tokenize(src) end def interpret(env) @ret = [] @block = [] idx = _eval(env, 0) unless @block.empty? raise "EOF: not closed block (#{@block.join(', ')})" end @ret.join('') end end # Interp end # Template end # HTML if $0 == __FILE__ template = HTML::Template.new('filename' => 'template.html') template.param( { 'name'=>'MoonWolf', 'mail'=>'moonwolf@moonwolf.com'} ) template.param( { 'loop'=>[{'rot'=>'1'}, {'rot'=>'2'}, {'rot'=>'3'}] } ) template.param( { 'text'=>"<./:>\t&%3D\n;"} ) print "Content-Type: text/html\r\n\r\n" print template.output end __END__ hoe hoemoe oddhoe