# 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