a5e08f7bcc
I installed the rails_xss plugin, for the main purpose of seeing what will break with Rails 3.0 (where the behaviour of the plugin is the default). I think I've fixed everything, but let me know if you see stuff that is HTML-escaped, which shouldn't be. As a side benefit, we now use Erubis, rather than ERB, to render templates. They tell me it's faster ...
3331 lines
78 KiB
Ruby
Executable file
3331 lines
78 KiB
Ruby
Executable file
#!/usr/bin/env ruby
|
|
|
|
###
|
|
### $Release: 2.6.5 $
|
|
### copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
###
|
|
|
|
#--begin of require 'erubis/main'
|
|
###
|
|
### $Release: 2.6.5 $
|
|
### copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
###
|
|
|
|
require 'yaml'
|
|
#--begin of require 'erubis'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
##
|
|
## an implementation of eRuby
|
|
##
|
|
## ex.
|
|
## input = <<'END'
|
|
## <ul>
|
|
## <% for item in @list %>
|
|
## <li><%= item %>
|
|
## <%== item %></li>
|
|
## <% end %>
|
|
## </ul>
|
|
## END
|
|
## list = ['<aaa>', 'b&b', '"ccc"']
|
|
## eruby = Erubis::Eruby.new(input)
|
|
## puts "--- code ---"
|
|
## puts eruby.src
|
|
## puts "--- result ---"
|
|
## context = Erubis::Context.new() # or new(:list=>list)
|
|
## context[:list] = list
|
|
## puts eruby.evaluate(context)
|
|
##
|
|
## result:
|
|
## --- source ---
|
|
## _buf = ''; _buf << '<ul>
|
|
## '; for item in @list
|
|
## _buf << ' <li>'; _buf << ( item ).to_s; _buf << '
|
|
## '; _buf << ' '; _buf << Erubis::XmlHelper.escape_xml( item ); _buf << '</li>
|
|
## '; end
|
|
## _buf << '</ul>
|
|
## ';
|
|
## _buf.to_s
|
|
## --- result ---
|
|
## <ul>
|
|
## <li><aaa>
|
|
## <aaa></li>
|
|
## <li>b&b
|
|
## b&b</li>
|
|
## <li>"ccc"
|
|
## "ccc"</li>
|
|
## </ul>
|
|
##
|
|
|
|
|
|
module Erubis
|
|
VERSION = ('$Release: 2.6.5 $' =~ /([.\d]+)/) && $1
|
|
end
|
|
|
|
#--begin of require 'erubis/engine'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
|
|
#--begin of require 'erubis/generator'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--begin of require 'abstract'
|
|
##
|
|
## $Rev: 1 $
|
|
## $Release: 0.1.0 $
|
|
## copyright(c) 2006 kuwata-lab.com all rights reserved.
|
|
##
|
|
##
|
|
## helper to define abstract method in Ruby.
|
|
##
|
|
##
|
|
## example1. (shorter notation)
|
|
##
|
|
## require 'abstract'
|
|
## class Foo
|
|
## abstract_method 'arg1, arg2=""', :method1, :method2, :method3
|
|
## end
|
|
##
|
|
##
|
|
## example2. (RDoc friendly notation)
|
|
##
|
|
## require 'abstract'
|
|
## class Bar
|
|
## # ... method1 description ...
|
|
## def method1(arg1, arg2="")
|
|
## not_implemented
|
|
## end
|
|
##
|
|
## # ... method2 description ...
|
|
## def method2(arg1, arg2="")
|
|
## not_implemented
|
|
## end
|
|
## end
|
|
##
|
|
|
|
|
|
##
|
|
class Module
|
|
|
|
##
|
|
## define abstract methods
|
|
##
|
|
def abstract_method args_str, *method_names
|
|
method_names.each do |name|
|
|
module_eval <<-END
|
|
def #{name}(#{args_str})
|
|
mesg = "class \#{self.class.name} must implement abstract method `#{self.name}##{name}()'."
|
|
#mesg = "\#{self.class.name}##{name}() is not implemented."
|
|
err = NotImplementedError.new mesg
|
|
err.set_backtrace caller()
|
|
raise err
|
|
end
|
|
END
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
module Kernel
|
|
|
|
##
|
|
## raise NotImplementedError
|
|
##
|
|
def not_implemented #:doc:
|
|
backtrace = caller()
|
|
method_name = (backtrace.shift =~ /`(\w+)'$/) && $1
|
|
mesg = "class #{self.class.name} must implement abstract method '#{method_name}()'."
|
|
#mesg = "#{self.class.name}##{method_name}() is not implemented."
|
|
err = NotImplementedError.new mesg
|
|
err.set_backtrace backtrace
|
|
raise err
|
|
end
|
|
private :not_implemented
|
|
|
|
end
|
|
#--end of require 'abstract'
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## code generator, called by Converter module
|
|
##
|
|
module Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return [
|
|
[:escapefunc, nil, "escape function name"],
|
|
]
|
|
end
|
|
|
|
attr_accessor :escapefunc
|
|
|
|
def init_generator(properties={})
|
|
@escapefunc = properties[:escapefunc]
|
|
end
|
|
|
|
|
|
## (abstract) escape text string
|
|
##
|
|
## ex.
|
|
## def escape_text(text)
|
|
## return text.dump
|
|
## # or return "'" + text.gsub(/['\\]/, '\\\\\&') + "'"
|
|
## end
|
|
def escape_text(text)
|
|
not_implemented
|
|
end
|
|
|
|
## return escaped expression code (ex. 'h(...)' or 'htmlspecialchars(...)')
|
|
def escaped_expr(code)
|
|
code.strip!
|
|
return "#{@escapefunc}(#{code})"
|
|
end
|
|
|
|
## (abstract) add @preamble to src
|
|
def add_preamble(src)
|
|
not_implemented
|
|
end
|
|
|
|
## (abstract) add text string to src
|
|
def add_text(src, text)
|
|
not_implemented
|
|
end
|
|
|
|
## (abstract) add statement code to src
|
|
def add_stmt(src, code)
|
|
not_implemented
|
|
end
|
|
|
|
## (abstract) add expression literal code to src. this is called by add_expr().
|
|
def add_expr_literal(src, code)
|
|
not_implemented
|
|
end
|
|
|
|
## (abstract) add escaped expression code to src. this is called by add_expr().
|
|
def add_expr_escaped(src, code)
|
|
not_implemented
|
|
end
|
|
|
|
## (abstract) add expression code to src for debug. this is called by add_expr().
|
|
def add_expr_debug(src, code)
|
|
not_implemented
|
|
end
|
|
|
|
## (abstract) add @postamble to src
|
|
def add_postamble(src)
|
|
not_implemented
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/generator'
|
|
#--begin of require 'erubis/converter'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'abstract'
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## convert
|
|
##
|
|
module Converter
|
|
|
|
attr_accessor :preamble, :postamble, :escape
|
|
|
|
def self.supported_properties # :nodoc:
|
|
return [
|
|
[:preamble, nil, "preamble (no preamble when false)"],
|
|
[:postamble, nil, "postamble (no postamble when false)"],
|
|
[:escape, nil, "escape expression or not in default"],
|
|
]
|
|
end
|
|
|
|
def init_converter(properties={})
|
|
@preamble = properties[:preamble]
|
|
@postamble = properties[:postamble]
|
|
@escape = properties[:escape]
|
|
end
|
|
|
|
## convert input string into target language
|
|
def convert(input)
|
|
codebuf = "" # or []
|
|
@preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
|
|
convert_input(codebuf, input)
|
|
@postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
|
|
@_proc = nil # clear cached proc object
|
|
return codebuf # or codebuf.join()
|
|
end
|
|
|
|
protected
|
|
|
|
##
|
|
## detect spaces at beginning of line
|
|
##
|
|
def detect_spaces_at_bol(text, is_bol)
|
|
lspace = nil
|
|
if text.empty?
|
|
lspace = "" if is_bol
|
|
elsif text[-1] == ?\n
|
|
lspace = ""
|
|
else
|
|
rindex = text.rindex(?\n)
|
|
if rindex
|
|
s = text[rindex+1..-1]
|
|
if s =~ /\A[ \t]*\z/
|
|
lspace = s
|
|
#text = text[0..rindex]
|
|
text[rindex+1..-1] = ''
|
|
end
|
|
else
|
|
if is_bol && text =~ /\A[ \t]*\z/
|
|
#lspace = text
|
|
#text = nil
|
|
lspace = text.dup
|
|
text[0..-1] = ''
|
|
end
|
|
end
|
|
end
|
|
return lspace
|
|
end
|
|
|
|
##
|
|
## (abstract) convert input to code
|
|
##
|
|
def convert_input(codebuf, input)
|
|
not_implemented
|
|
end
|
|
|
|
end
|
|
|
|
|
|
module Basic
|
|
end
|
|
|
|
|
|
##
|
|
## basic converter which supports '<% ... %>' notation.
|
|
##
|
|
module Basic::Converter
|
|
include Erubis::Converter
|
|
|
|
def self.supported_properties # :nodoc:
|
|
return [
|
|
[:pattern, '<% %>', "embed pattern"],
|
|
[:trim, true, "trim spaces around <% ... %>"],
|
|
]
|
|
end
|
|
|
|
attr_accessor :pattern, :trim
|
|
|
|
def init_converter(properties={})
|
|
super(properties)
|
|
@pattern = properties[:pattern]
|
|
@trim = properties[:trim] != false
|
|
end
|
|
|
|
protected
|
|
|
|
## return regexp of pattern to parse eRuby script
|
|
def pattern_regexp(pattern)
|
|
@prefix, @postfix = pattern.split() # '<% %>' => '<%', '%>'
|
|
#return /(.*?)(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
|
|
#return /(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
|
|
return /#{@prefix}(=+|-|\#|%)?(.*?)([-=])?#{@postfix}([ \t]*\r?\n)?/m
|
|
end
|
|
module_function :pattern_regexp
|
|
|
|
#DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
|
|
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
|
|
#DEFAULT_REGEXP = /<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
|
|
DEFAULT_REGEXP = pattern_regexp('<% %>')
|
|
|
|
public
|
|
|
|
def convert_input(src, input)
|
|
pat = @pattern
|
|
regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
|
|
pos = 0
|
|
is_bol = true # is beginning of line
|
|
input.scan(regexp) do |indicator, code, tailch, rspace|
|
|
match = Regexp.last_match()
|
|
len = match.begin(0) - pos
|
|
text = input[pos, len]
|
|
pos = match.end(0)
|
|
ch = indicator ? indicator[0] : nil
|
|
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
|
|
is_bol = rspace ? true : false
|
|
add_text(src, text) if text && !text.empty?
|
|
## * when '<%= %>', do nothing
|
|
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
|
|
if ch == ?= # <%= %>
|
|
rspace = nil if tailch && !tailch.empty?
|
|
add_text(src, lspace) if lspace
|
|
add_expr(src, code, indicator)
|
|
add_text(src, rspace) if rspace
|
|
elsif ch == ?\# # <%# %>
|
|
n = code.count("\n") + (rspace ? 1 : 0)
|
|
if @trim && lspace && rspace
|
|
add_stmt(src, "\n" * n)
|
|
else
|
|
add_text(src, lspace) if lspace
|
|
add_stmt(src, "\n" * n)
|
|
add_text(src, rspace) if rspace
|
|
end
|
|
elsif ch == ?% # <%% %>
|
|
s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
|
|
add_text(src, s)
|
|
else # <% %>
|
|
if @trim && lspace && rspace
|
|
add_stmt(src, "#{lspace}#{code}#{rspace}")
|
|
else
|
|
add_text(src, lspace) if lspace
|
|
add_stmt(src, code)
|
|
add_text(src, rspace) if rspace
|
|
end
|
|
end
|
|
end
|
|
#rest = $' || input # ruby1.8
|
|
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
|
add_text(src, rest)
|
|
end
|
|
|
|
## add expression code to src
|
|
def add_expr(src, code, indicator)
|
|
case indicator
|
|
when '='
|
|
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
|
|
when '=='
|
|
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
|
|
when '==='
|
|
add_expr_debug(src, code)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
module PI
|
|
end
|
|
|
|
##
|
|
## Processing Instructions (PI) converter for XML.
|
|
## this class converts '<?rb ... ?>' and '${...}' notation.
|
|
##
|
|
module PI::Converter
|
|
include Erubis::Converter
|
|
|
|
def self.desc # :nodoc:
|
|
"use processing instructions (PI) instead of '<% %>'"
|
|
end
|
|
|
|
def self.supported_properties # :nodoc:
|
|
return [
|
|
[:trim, true, "trim spaces around <% ... %>"],
|
|
[:pi, 'rb', "PI (Processing Instrunctions) name"],
|
|
[:embchar, '@', "char for embedded expression pattern('@{...}@')"],
|
|
[:pattern, '<% %>', "embed pattern"],
|
|
]
|
|
end
|
|
|
|
attr_accessor :pi, :prefix
|
|
|
|
def init_converter(properties={})
|
|
super(properties)
|
|
@trim = properties.fetch(:trim, true)
|
|
@pi = properties[:pi] if properties[:pi]
|
|
@embchar = properties[:embchar] || '@'
|
|
@pattern = properties[:pattern]
|
|
@pattern = '<% %>' if @pattern.nil? #|| @pattern == true
|
|
end
|
|
|
|
def convert(input)
|
|
code = super(input)
|
|
return @header || @footer ? "#{@header}#{code}#{@footer}" : code
|
|
end
|
|
|
|
protected
|
|
|
|
def convert_input(codebuf, input)
|
|
unless @regexp
|
|
@pi ||= 'e'
|
|
ch = Regexp.escape(@embchar)
|
|
if @pattern
|
|
left, right = @pattern.split(' ')
|
|
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
|
|
else
|
|
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
|
|
end
|
|
end
|
|
#
|
|
is_bol = true
|
|
pos = 0
|
|
input.scan(@regexp) do |pi_arg, stmt, rspace,
|
|
indicator1, expr1, indicator2, expr2|
|
|
match = Regexp.last_match
|
|
len = match.begin(0) - pos
|
|
text = input[pos, len]
|
|
pos = match.end(0)
|
|
lspace = stmt ? detect_spaces_at_bol(text, is_bol) : nil
|
|
is_bol = stmt && rspace ? true : false
|
|
add_text(codebuf, text) # unless text.empty?
|
|
#
|
|
if stmt
|
|
if @trim && lspace && rspace
|
|
add_pi_stmt(codebuf, "#{lspace}#{stmt}#{rspace}", pi_arg)
|
|
else
|
|
add_text(codebuf, lspace) if lspace
|
|
add_pi_stmt(codebuf, stmt, pi_arg)
|
|
add_text(codebuf, rspace) if rspace
|
|
end
|
|
else
|
|
add_pi_expr(codebuf, expr1 || expr2, indicator1 || indicator2)
|
|
end
|
|
end
|
|
#rest = $' || input # ruby1.8
|
|
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
|
add_text(codebuf, rest)
|
|
end
|
|
|
|
#--
|
|
#def convert_input(codebuf, input)
|
|
# parse_stmts(codebuf, input)
|
|
# #parse_stmts2(codebuf, input)
|
|
#end
|
|
#
|
|
#def parse_stmts(codebuf, input)
|
|
# #regexp = pattern_regexp(@pattern)
|
|
# @pi ||= 'e'
|
|
# @stmt_pattern ||= /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
|
|
# is_bol = true
|
|
# pos = 0
|
|
# input.scan(@stmt_pattern) do |pi_arg, code, rspace|
|
|
# match = Regexp.last_match
|
|
# len = match.begin(0) - pos
|
|
# text = input[pos, len]
|
|
# pos = match.end(0)
|
|
# lspace = detect_spaces_at_bol(text, is_bol)
|
|
# is_bol = rspace ? true : false
|
|
# parse_exprs(codebuf, text) # unless text.empty?
|
|
# if @trim && lspace && rspace
|
|
# add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
|
|
# else
|
|
# add_text(codebuf, lspace)
|
|
# add_pi_stmt(codebuf, code, pi_arg)
|
|
# add_text(codebuf, rspace)
|
|
# end
|
|
# end
|
|
# rest = $' || input
|
|
# parse_exprs(codebuf, rest)
|
|
#end
|
|
#
|
|
#def parse_exprs(codebuf, input)
|
|
# unless @expr_pattern
|
|
# ch = Regexp.escape(@embchar)
|
|
# if @pattern
|
|
# left, right = @pattern.split(' ')
|
|
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
|
|
# else
|
|
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
|
|
# end
|
|
# end
|
|
# pos = 0
|
|
# input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
|
|
# indicator = indicator1 || indicator2
|
|
# code = code1 || code2
|
|
# match = Regexp.last_match
|
|
# len = match.begin(0) - pos
|
|
# text = input[pos, len]
|
|
# pos = match.end(0)
|
|
# add_text(codebuf, text) # unless text.empty?
|
|
# add_pi_expr(codebuf, code, indicator)
|
|
# end
|
|
# rest = $' || input
|
|
# add_text(codebuf, rest)
|
|
#end
|
|
#++
|
|
|
|
def add_pi_stmt(codebuf, code, pi_arg) # :nodoc:
|
|
case pi_arg
|
|
when nil ; add_stmt(codebuf, code)
|
|
when 'header' ; @header = code
|
|
when 'footer' ; @footer = code
|
|
when 'comment'; add_stmt(codebuf, "\n" * code.count("\n"))
|
|
when 'value' ; add_expr_literal(codebuf, code)
|
|
else ; add_stmt(codebuf, code)
|
|
end
|
|
end
|
|
|
|
def add_pi_expr(codebuf, code, indicator) # :nodoc:
|
|
case indicator
|
|
when nil, '', '==' # @{...}@ or <%== ... %>
|
|
@escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
|
|
when '!', '=' # @!{...}@ or <%= ... %>
|
|
@escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
|
|
when '!!', '===' # @!!{...}@ or <%=== ... %>
|
|
add_expr_debug(codebuf, code)
|
|
else
|
|
# ignore
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/converter'
|
|
#--begin of require 'erubis/evaluator'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--begin of require 'erubis/error'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## base error class
|
|
##
|
|
class ErubisError < StandardError
|
|
end
|
|
|
|
|
|
##
|
|
## raised when method or function is not supported
|
|
##
|
|
class NotSupportedError < ErubisError
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/error'
|
|
#--begin of require 'erubis/context'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## context object for Engine#evaluate
|
|
##
|
|
## ex.
|
|
## template = <<'END'
|
|
## Hello <%= @user %>!
|
|
## <% for item in @list %>
|
|
## - <%= item %>
|
|
## <% end %>
|
|
## END
|
|
##
|
|
## context = Erubis::Context.new(:user=>'World', :list=>['a','b','c'])
|
|
## # or
|
|
## # context = Erubis::Context.new
|
|
## # context[:user] = 'World'
|
|
## # context[:list] = ['a', 'b', 'c']
|
|
##
|
|
## eruby = Erubis::Eruby.new(template)
|
|
## print eruby.evaluate(context)
|
|
##
|
|
class Context
|
|
include Enumerable
|
|
|
|
def initialize(hash=nil)
|
|
hash.each do |name, value|
|
|
self[name] = value
|
|
end if hash
|
|
end
|
|
|
|
def [](key)
|
|
return instance_variable_get("@#{key}")
|
|
end
|
|
|
|
def []=(key, value)
|
|
return instance_variable_set("@#{key}", value)
|
|
end
|
|
|
|
def keys
|
|
return instance_variables.collect { |name| name[1..-1] }
|
|
end
|
|
|
|
def each
|
|
instance_variables.each do |name|
|
|
key = name[1..-1]
|
|
value = instance_variable_get(name)
|
|
yield(key, value)
|
|
end
|
|
end
|
|
|
|
def to_hash
|
|
hash = {}
|
|
self.keys.each { |key| hash[key] = self[key] }
|
|
return hash
|
|
end
|
|
|
|
def update(context_or_hash)
|
|
arg = context_or_hash
|
|
if arg.is_a?(Hash)
|
|
arg.each do |key, val|
|
|
self[key] = val
|
|
end
|
|
else
|
|
arg.instance_variables.each do |varname|
|
|
key = varname[1..-1]
|
|
val = arg.instance_variable_get(varname)
|
|
self[key] = val
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/context'
|
|
|
|
|
|
module Erubis
|
|
|
|
EMPTY_BINDING = binding()
|
|
|
|
|
|
##
|
|
## evaluate code
|
|
##
|
|
module Evaluator
|
|
|
|
def self.supported_properties # :nodoc:
|
|
return []
|
|
end
|
|
|
|
attr_accessor :src, :filename
|
|
|
|
def init_evaluator(properties)
|
|
@filename = properties[:filename]
|
|
end
|
|
|
|
def result(*args)
|
|
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
|
|
end
|
|
|
|
def evaluate(*args)
|
|
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## evaluator for Ruby
|
|
##
|
|
module RubyEvaluator
|
|
include Evaluator
|
|
|
|
def self.supported_properties # :nodoc:
|
|
list = Evaluator.supported_properties
|
|
return list
|
|
end
|
|
|
|
## eval(@src) with binding object
|
|
def result(_binding_or_hash=TOPLEVEL_BINDING)
|
|
_arg = _binding_or_hash
|
|
if _arg.is_a?(Hash)
|
|
_b = binding()
|
|
eval _arg.collect{|k,v| "#{k} = _arg[#{k.inspect}]; "}.join, _b
|
|
elsif _arg.is_a?(Binding)
|
|
_b = _arg
|
|
elsif _arg.nil?
|
|
_b = binding()
|
|
else
|
|
raise ArgumentError.new("#{self.class.name}#result(): argument should be Binding or Hash but passed #{_arg.class.name} object.")
|
|
end
|
|
return eval(@src, _b, (@filename || '(erubis'))
|
|
end
|
|
|
|
## invoke context.instance_eval(@src)
|
|
def evaluate(_context=Context.new)
|
|
_context = Context.new(_context) if _context.is_a?(Hash)
|
|
#return _context.instance_eval(@src, @filename || '(erubis)')
|
|
#@_proc ||= eval("proc { #{@src} }", Erubis::EMPTY_BINDING, @filename || '(erubis)')
|
|
@_proc ||= eval("proc { #{@src} }", binding(), @filename || '(erubis)')
|
|
return _context.instance_eval(&@_proc)
|
|
end
|
|
|
|
## if object is an Class or Module then define instance method to it,
|
|
## else define singleton method to it.
|
|
def def_method(object, method_name, filename=nil)
|
|
m = object.is_a?(Module) ? :module_eval : :instance_eval
|
|
object.__send__(m, "def #{method_name}; #{@src}; end", filename || @filename || '(erubis)')
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/evaluator'
|
|
#--already included require 'erubis/context'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## (abstract) abstract engine class.
|
|
## subclass must include evaluator and converter module.
|
|
##
|
|
class Engine
|
|
#include Evaluator
|
|
#include Converter
|
|
#include Generator
|
|
|
|
def initialize(input=nil, properties={})
|
|
#@input = input
|
|
init_generator(properties)
|
|
init_converter(properties)
|
|
init_evaluator(properties)
|
|
@src = convert(input) if input
|
|
end
|
|
|
|
|
|
##
|
|
## convert input string and set it to @src
|
|
##
|
|
def convert!(input)
|
|
@src = convert(input)
|
|
end
|
|
|
|
|
|
##
|
|
## load file, write cache file, and return engine object.
|
|
## this method create code cache file automatically.
|
|
## cachefile name can be specified with properties[:cachename],
|
|
## or filname + 'cache' is used as default.
|
|
##
|
|
def self.load_file(filename, properties={})
|
|
cachename = properties[:cachename] || (filename + '.cache')
|
|
properties[:filename] = filename
|
|
if test(?f, cachename) && File.mtime(filename) <= File.mtime(cachename)
|
|
engine = self.new(nil, properties)
|
|
engine.src = File.read(cachename)
|
|
else
|
|
input = File.open(filename, 'rb') {|f| f.read }
|
|
engine = self.new(input, properties)
|
|
File.open(cachename, 'wb') do |f|
|
|
f.flock(File::LOCK_EX)
|
|
f.write(engine.src)
|
|
f.flush()
|
|
end
|
|
end
|
|
engine.src.untaint # ok?
|
|
return engine
|
|
end
|
|
|
|
|
|
##
|
|
## helper method to convert and evaluate input text with context object.
|
|
## context may be Binding, Hash, or Object.
|
|
##
|
|
def process(input, context=nil, filename=nil)
|
|
code = convert(input)
|
|
filename ||= '(erubis)'
|
|
if context.is_a?(Binding)
|
|
return eval(code, context, filename)
|
|
else
|
|
context = Context.new(context) if context.is_a?(Hash)
|
|
return context.instance_eval(code, filename)
|
|
end
|
|
end
|
|
|
|
|
|
##
|
|
## helper method evaluate Proc object with contect object.
|
|
## context may be Binding, Hash, or Object.
|
|
##
|
|
def process_proc(proc_obj, context=nil, filename=nil)
|
|
if context.is_a?(Binding)
|
|
filename ||= '(erubis)'
|
|
return eval(proc_obj, context, filename)
|
|
else
|
|
context = Context.new(context) if context.is_a?(Hash)
|
|
return context.instance_eval(&proc_obj)
|
|
end
|
|
end
|
|
|
|
|
|
end # end of class Engine
|
|
|
|
|
|
##
|
|
## (abstract) base engine class for Eruby, Eperl, Ejava, and so on.
|
|
## subclass must include generator.
|
|
##
|
|
class Basic::Engine < Engine
|
|
include Evaluator
|
|
include Basic::Converter
|
|
include Generator
|
|
end
|
|
|
|
|
|
class PI::Engine < Engine
|
|
include Evaluator
|
|
include PI::Converter
|
|
include Generator
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine'
|
|
#require 'erubis/generator'
|
|
#require 'erubis/converter'
|
|
#require 'erubis/evaluator'
|
|
#require 'erubis/error'
|
|
#require 'erubis/context'
|
|
#--begin of require 'erubis/helper'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
|
|
module Erubis
|
|
|
|
##
|
|
## helper for xml
|
|
##
|
|
module XmlHelper
|
|
|
|
module_function
|
|
|
|
ESCAPE_TABLE = {
|
|
'&' => '&',
|
|
'<' => '<',
|
|
'>' => '>',
|
|
'"' => '"',
|
|
"'" => ''',
|
|
}
|
|
|
|
def escape_xml(value)
|
|
value.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] } # or /[&<>"']/
|
|
#value.to_s.gsub(/[&<>"]/) { ESCAPE_TABLE[$&] }
|
|
end
|
|
|
|
def escape_xml2(value)
|
|
return value.to_s.gsub(/\&/,'&').gsub(/</,'<').gsub(/>/,'>').gsub(/"/,'"')
|
|
end
|
|
|
|
alias h escape_xml
|
|
alias html_escape escape_xml
|
|
|
|
def url_encode(str)
|
|
return str.gsub(/[^-_.a-zA-Z0-9]+/) { |s|
|
|
s.unpack('C*').collect { |i| "%%%02X" % i }.join
|
|
}
|
|
end
|
|
|
|
alias u url_encode
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/helper'
|
|
#--begin of require 'erubis/enhancer'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
|
|
##
|
|
## ex.
|
|
## class XmlEruby < Eruby
|
|
## include EscapeEnhancer
|
|
## end
|
|
##
|
|
## this is language-indenedent.
|
|
##
|
|
module EscapeEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"switch '<%= %>' to escaped and '<%== %>' to unescaped"
|
|
end
|
|
|
|
#--
|
|
#def self.included(klass)
|
|
# klass.class_eval <<-END
|
|
# alias _add_expr_literal add_expr_literal
|
|
# alias _add_expr_escaped add_expr_escaped
|
|
# alias add_expr_literal _add_expr_escaped
|
|
# alias add_expr_escaped _add_expr_literal
|
|
# END
|
|
#end
|
|
#++
|
|
|
|
def add_expr(src, code, indicator)
|
|
case indicator
|
|
when '='
|
|
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
|
|
when '=='
|
|
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
|
|
when '==='
|
|
add_expr_debug(src, code)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
#--
|
|
## (obsolete)
|
|
#module FastEnhancer
|
|
#end
|
|
#++
|
|
|
|
|
|
##
|
|
## use $stdout instead of string
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module StdoutEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"use $stdout instead of array buffer or string buffer"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "_buf = $stdout;"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n''\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## use print statement instead of '_buf << ...'
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module PrintOutEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"use print statement instead of '_buf << ...'"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
end
|
|
|
|
def add_text(src, text)
|
|
src << " print '" << escape_text(text) << "';" unless text.empty?
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
src << ' print((' << code << ').to_s);'
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
src << ' print ' << escaped_expr(code) << ';'
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## enable print function
|
|
##
|
|
## Notice: use Eruby#evaluate() and don't use Eruby#result()
|
|
## to be enable print function.
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module PrintEnabledEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"enable to use print function in '<% %>'"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "@_buf = "
|
|
super
|
|
end
|
|
|
|
def print(*args)
|
|
args.each do |arg|
|
|
@_buf << arg.to_s
|
|
end
|
|
end
|
|
|
|
def evaluate(context=nil)
|
|
_src = @src
|
|
if context.is_a?(Hash)
|
|
context.each do |key, val| instance_variable_set("@#{key}", val) end
|
|
elsif context
|
|
context.instance_variables.each do |name|
|
|
instance_variable_set(name, context.instance_variable_get(name))
|
|
end
|
|
end
|
|
return instance_eval(_src, (@filename || '(erubis)'))
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## return array instead of string
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module ArrayEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"return array instead of string"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "_buf = [];"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
src << "_buf\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## use an Array object as buffer (included in Eruby by default)
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module ArrayBufferEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"use an Array object for buffering (included in Eruby class)"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "_buf = [];"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
src << "_buf.join\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## use String class for buffering
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module StringBufferEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"use a String object for buffering"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "_buf = '';"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
src << "_buf.to_s\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## use StringIO class for buffering
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module StringIOEnhancer # :nodoc:
|
|
|
|
def self.desc # :nodoc:
|
|
"use a StringIO object for buffering"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "_buf = StringIO.new;"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
src << "_buf.string\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## set buffer variable name to '_erbout' as well as '_buf'
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module ErboutEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"set '_erbout = _buf = \"\";' to be compatible with ERB."
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "_erbout = _buf = '';"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
src << "_buf.to_s\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## remove text and leave code, especially useful when debugging.
|
|
##
|
|
## ex.
|
|
## $ erubis -s -E NoText file.eruby | more
|
|
##
|
|
## this is language independent.
|
|
##
|
|
module NoTextEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"remove text and leave code (useful when debugging)"
|
|
end
|
|
|
|
def add_text(src, text)
|
|
src << ("\n" * text.count("\n"))
|
|
if text[-1] != ?\n
|
|
text =~ /^(.*?)\z/
|
|
src << (' ' * $1.length)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## remove code and leave text, especially useful when validating HTML tags.
|
|
##
|
|
## ex.
|
|
## $ erubis -s -E NoCode file.eruby | tidy -errors
|
|
##
|
|
## this is language independent.
|
|
##
|
|
module NoCodeEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"remove code and leave text (useful when validating HTML)"
|
|
end
|
|
|
|
def add_preamble(src)
|
|
end
|
|
|
|
def add_postamble(src)
|
|
end
|
|
|
|
def add_text(src, text)
|
|
src << text
|
|
end
|
|
|
|
def add_expr(src, code, indicator)
|
|
src << "\n" * code.count("\n")
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << "\n" * code.count("\n")
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## get convert faster, but spaces around '<%...%>' are not trimmed.
|
|
##
|
|
## this is language-independent.
|
|
##
|
|
module SimplifyEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"get convert faster but leave spaces around '<% %>'"
|
|
end
|
|
|
|
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
|
|
SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m
|
|
|
|
def convert(input)
|
|
src = ""
|
|
add_preamble(src)
|
|
#regexp = pattern_regexp(@pattern)
|
|
pos = 0
|
|
input.scan(SIMPLE_REGEXP) do |indicator, code|
|
|
match = Regexp.last_match
|
|
index = match.begin(0)
|
|
text = input[pos, index - pos]
|
|
pos = match.end(0)
|
|
add_text(src, text)
|
|
if !indicator # <% %>
|
|
add_stmt(src, code)
|
|
elsif indicator[0] == ?\# # <%# %>
|
|
n = code.count("\n")
|
|
add_stmt(src, "\n" * n)
|
|
else # <%= %>
|
|
add_expr(src, code, indicator)
|
|
end
|
|
end
|
|
#rest = $' || input # ruby1.8
|
|
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
|
add_text(src, rest)
|
|
add_postamble(src)
|
|
return src
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## enable to use other embedded expression pattern (default is '\[= =\]').
|
|
##
|
|
## notice! this is an experimental. spec may change in the future.
|
|
##
|
|
## ex.
|
|
## input = <<END
|
|
## <% for item in list %>
|
|
## <%= item %> : <%== item %>
|
|
## [= item =] : [== item =]
|
|
## <% end %>
|
|
## END
|
|
##
|
|
## class BiPatternEruby
|
|
## include BiPatternEnhancer
|
|
## end
|
|
## eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
|
|
## list = ['<a>', 'b&b', '"c"']
|
|
## print eruby.result(binding())
|
|
##
|
|
## ## output
|
|
## <a> : <a>
|
|
## <a> : <a>
|
|
## b&b : b&b
|
|
## b&b : b&b
|
|
## "c" : "c"
|
|
## "c" : "c"
|
|
##
|
|
## this is language independent.
|
|
##
|
|
module BiPatternEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"another embedded expression pattern (default '\[= =\]')."
|
|
end
|
|
|
|
def initialize(input, properties={})
|
|
self.bipattern = properties[:bipattern] # or '\$\{ \}'
|
|
super
|
|
end
|
|
|
|
## when pat is nil then '\[= =\]' is used
|
|
def bipattern=(pat) # :nodoc:
|
|
@bipattern = pat || '\[= =\]'
|
|
pre, post = @bipattern.split()
|
|
@bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return unless text
|
|
m = nil
|
|
text.scan(@bipattern_regexp) do |txt, indicator, code|
|
|
m = Regexp.last_match
|
|
super(src, txt)
|
|
add_expr(src, code, '=' + indicator)
|
|
end
|
|
#rest = $' || text # ruby1.8
|
|
rest = m ? text[m.end(0)..-1] : text # ruby1.9
|
|
super(src, rest)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## regards lines starting with '%' as program code
|
|
##
|
|
## this is for compatibility to eruby and ERB.
|
|
##
|
|
## this is language-independent.
|
|
##
|
|
module PercentLineEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"regard lines starting with '%' as program code"
|
|
end
|
|
|
|
def add_text(src, text)
|
|
pos = 0
|
|
text2 = ''
|
|
text.scan(/^\%(.*?\r?\n)/) do
|
|
line = $1
|
|
match = Regexp.last_match
|
|
len = match.begin(0) - pos
|
|
str = text[pos, len]
|
|
pos = match.end(0)
|
|
if text2.empty?
|
|
text2 = str
|
|
else
|
|
text2 << str
|
|
end
|
|
if line[0] == ?%
|
|
text2 << line
|
|
else
|
|
super(src, text2)
|
|
text2 = ''
|
|
add_stmt(src, line)
|
|
end
|
|
end
|
|
#rest = pos == 0 ? text : $' # ruby1.8
|
|
rest = pos == 0 ? text : text[pos..-1] # ruby1.9
|
|
unless text2.empty?
|
|
text2 << rest if rest
|
|
rest = text2
|
|
end
|
|
super(src, rest)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## [experimental] allow header and footer in eRuby script
|
|
##
|
|
## ex.
|
|
## ====================
|
|
## ## without header and footer
|
|
## $ cat ex1.eruby
|
|
## <% def list_items(list) %>
|
|
## <% for item in list %>
|
|
## <li><%= item %></li>
|
|
## <% end %>
|
|
## <% end %>
|
|
##
|
|
## $ erubis -s ex1.eruby
|
|
## _buf = []; def list_items(list)
|
|
## ; for item in list
|
|
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
|
|
## '; end
|
|
## ; end
|
|
## ;
|
|
## _buf.join
|
|
##
|
|
## ## with header and footer
|
|
## $ cat ex2.eruby
|
|
## <!--#header:
|
|
## def list_items(list)
|
|
## #-->
|
|
## <% for item in list %>
|
|
## <li><%= item %></li>
|
|
## <% end %>
|
|
## <!--#footer:
|
|
## end
|
|
## #-->
|
|
##
|
|
## $ erubis -s -c HeaderFooterEruby ex4.eruby
|
|
##
|
|
## def list_items(list)
|
|
## _buf = []; _buf << '
|
|
## '; for item in list
|
|
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
|
|
## '; end
|
|
## ; _buf << '
|
|
## ';
|
|
## _buf.join
|
|
## end
|
|
##
|
|
## ====================
|
|
##
|
|
## this is language-independent.
|
|
##
|
|
module HeaderFooterEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"allow header/footer in document (ex. '<!--#header: #-->')"
|
|
end
|
|
|
|
HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m
|
|
|
|
def add_text(src, text)
|
|
m = nil
|
|
text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
|
|
m = Regexp.last_match
|
|
flag_trim = @trim && lspace && rspace
|
|
super(src, txt)
|
|
content = "#{lspace}#{content}#{rspace}" if flag_trim
|
|
super(src, lspace) if !flag_trim && lspace
|
|
instance_variable_set("@#{word}", content)
|
|
super(src, rspace) if !flag_trim && rspace
|
|
end
|
|
#rest = $' || text # ruby1.8
|
|
rest = m ? text[m.end(0)..-1] : text # ruby1.9
|
|
super(src, rest)
|
|
end
|
|
|
|
attr_accessor :header, :footer
|
|
|
|
def convert(input)
|
|
source = super
|
|
return @src = "#{@header}#{source}#{@footer}"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## delete indentation of HTML.
|
|
##
|
|
## this is language-independent.
|
|
##
|
|
module DeleteIndentEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"delete indentation of HTML."
|
|
end
|
|
|
|
def convert_input(src, input)
|
|
input = input.gsub(/^[ \t]+</, '<')
|
|
super(src, input)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## convert "<h1><%=title%></h1>" into "_buf << %Q`<h1>#{title}</h1>`"
|
|
##
|
|
## this is only for Eruby.
|
|
##
|
|
module InterpolationEnhancer
|
|
|
|
def self.desc # :nodoc:
|
|
"convert '<p><%=text%></p>' into '_buf << %Q`<p>\#{text}</p>`'"
|
|
end
|
|
|
|
def convert_input(src, input)
|
|
pat = @pattern
|
|
regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat)
|
|
pos = 0
|
|
is_bol = true # is beginning of line
|
|
str = ''
|
|
input.scan(regexp) do |indicator, code, tailch, rspace|
|
|
match = Regexp.last_match()
|
|
len = match.begin(0) - pos
|
|
text = input[pos, len]
|
|
pos = match.end(0)
|
|
ch = indicator ? indicator[0] : nil
|
|
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
|
|
is_bol = rspace ? true : false
|
|
_add_text_to_str(str, text)
|
|
## * when '<%= %>', do nothing
|
|
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
|
|
if ch == ?= # <%= %>
|
|
rspace = nil if tailch && !tailch.empty?
|
|
str << lspace if lspace
|
|
add_expr(str, code, indicator)
|
|
str << rspace if rspace
|
|
elsif ch == ?\# # <%# %>
|
|
n = code.count("\n") + (rspace ? 1 : 0)
|
|
if @trim && lspace && rspace
|
|
add_text(src, str)
|
|
str = ''
|
|
add_stmt(src, "\n" * n)
|
|
else
|
|
str << lspace if lspace
|
|
add_text(src, str)
|
|
str = ''
|
|
add_stmt(src, "\n" * n)
|
|
str << rspace if rspace
|
|
end
|
|
else # <% %>
|
|
if @trim && lspace && rspace
|
|
add_text(src, str)
|
|
str = ''
|
|
add_stmt(src, "#{lspace}#{code}#{rspace}")
|
|
else
|
|
str << lspace if lspace
|
|
add_text(src, str)
|
|
str = ''
|
|
add_stmt(src, code)
|
|
str << rspace if rspace
|
|
end
|
|
end
|
|
end
|
|
#rest = $' || input # ruby1.8
|
|
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
|
_add_text_to_str(str, rest)
|
|
add_text(src, str)
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if !text || text.empty?
|
|
#src << " _buf << %Q`" << text << "`;"
|
|
if text[-1] == ?\n
|
|
text[-1] = "\\n"
|
|
src << " _buf << %Q`" << text << "`\n"
|
|
else
|
|
src << " _buf << %Q`" << text << "`;"
|
|
end
|
|
end
|
|
|
|
def _add_text_to_str(str, text)
|
|
return if !text || text.empty?
|
|
text.gsub!(/['\#\\]/, '\\\\\&')
|
|
str << text
|
|
end
|
|
|
|
def add_expr_escaped(str, code)
|
|
str << "\#{#{escaped_expr(code)}}"
|
|
end
|
|
|
|
def add_expr_literal(str, code)
|
|
str << "\#{#{code}}"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/enhancer'
|
|
#require 'erubis/tiny'
|
|
#--begin of require 'erubis/engine/eruby'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
##
|
|
## code generator for Ruby
|
|
##
|
|
module RubyGenerator
|
|
include Generator
|
|
#include ArrayBufferEnhancer
|
|
include StringBufferEnhancer
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= "Erubis::XmlHelper.escape_xml"
|
|
end
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return []
|
|
end
|
|
|
|
def escape_text(text)
|
|
text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\'
|
|
end
|
|
|
|
def escaped_expr(code)
|
|
return "#{@escapefunc}(#{code})"
|
|
end
|
|
|
|
#--
|
|
#def add_preamble(src)
|
|
# src << "_buf = [];"
|
|
#end
|
|
#++
|
|
|
|
def add_text(src, text)
|
|
src << " _buf << '" << escape_text(text) << "';" unless text.empty?
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
#src << code << ';'
|
|
src << code
|
|
src << ';' unless code[-1] == ?\n
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
src << ' _buf << (' << code << ').to_s;'
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
src << ' _buf << ' << escaped_expr(code) << ';'
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
code.strip!
|
|
s = (code.dump =~ /\A"(.*)"\z/) && $1
|
|
src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
|
|
end
|
|
|
|
#--
|
|
#def add_postamble(src)
|
|
# src << "\n_buf.join\n"
|
|
#end
|
|
#++
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for Ruby
|
|
##
|
|
class Eruby < Basic::Engine
|
|
include RubyEvaluator
|
|
include RubyGenerator
|
|
end
|
|
|
|
|
|
##
|
|
## fast engine for Ruby
|
|
##
|
|
class FastEruby < Eruby
|
|
include InterpolationEnhancer
|
|
end
|
|
|
|
|
|
##
|
|
## swtich '<%= %>' to escaped and '<%== %>' to not escaped
|
|
##
|
|
class EscapedEruby < Eruby
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
##
|
|
## sanitize expression (<%= ... %>) by default
|
|
##
|
|
## this is equivalent to EscapedEruby and is prepared only for compatibility.
|
|
##
|
|
class XmlEruby < Eruby
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
class PI::Eruby < PI::Engine
|
|
include RubyEvaluator
|
|
include RubyGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'rb'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/eruby'
|
|
#require 'erubis/engine/enhanced' # enhanced eruby engines
|
|
#require 'erubis/engine/optimized' # generates optimized ruby code
|
|
#require 'erubis/engine/ephp'
|
|
#require 'erubis/engine/ec'
|
|
#require 'erubis/engine/ejava'
|
|
#require 'erubis/engine/escheme'
|
|
#require 'erubis/engine/eperl'
|
|
#require 'erubis/engine/ejavascript'
|
|
|
|
#--begin of require 'erubis/local-setting'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
##
|
|
## you can add site-local settings here.
|
|
## this files is required by erubis.rb
|
|
##
|
|
#--end of require 'erubis/local-setting'
|
|
#--end of require 'erubis'
|
|
#--begin of require 'erubis/tiny'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
module Erubis
|
|
|
|
##
|
|
## tiny and the simplest implementation of eRuby
|
|
##
|
|
## ex.
|
|
## eruby = TinyEruby.new(File.read('example.rhtml'))
|
|
## print eruby.src # print ruby code
|
|
## print eruby.result(binding()) # eval ruby code with Binding object
|
|
## print eruby.evalute(context) # eval ruby code with context object
|
|
##
|
|
class TinyEruby
|
|
|
|
def initialize(input=nil)
|
|
@src = convert(input) if input
|
|
end
|
|
attr_reader :src
|
|
|
|
EMBEDDED_PATTERN = /<%(=+|\#)?(.*?)-?%>/m
|
|
|
|
def convert(input)
|
|
src = "_buf = '';" # preamble
|
|
pos = 0
|
|
input.scan(EMBEDDED_PATTERN) do |indicator, code|
|
|
m = Regexp.last_match
|
|
text = input[pos...m.begin(0)]
|
|
pos = m.end(0)
|
|
#src << " _buf << '" << escape_text(text) << "';"
|
|
text.gsub!(/['\\]/, '\\\\\&')
|
|
src << " _buf << '" << text << "';" unless text.empty?
|
|
if !indicator # <% %>
|
|
src << code << ";"
|
|
elsif indicator == '#' # <%# %>
|
|
src << ("\n" * code.count("\n"))
|
|
else # <%= %>
|
|
src << " _buf << (" << code << ").to_s;"
|
|
end
|
|
end
|
|
#rest = $' || input # ruby1.8
|
|
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
|
#src << " _buf << '" << escape_text(rest) << "';"
|
|
rest.gsub!(/['\\]/, '\\\\\&')
|
|
src << " _buf << '" << rest << "';" unless rest.empty?
|
|
src << "\n_buf.to_s\n" # postamble
|
|
return src
|
|
end
|
|
|
|
#def escape_text(text)
|
|
# return text.gsub!(/['\\]/, '\\\\\&') || text
|
|
#end
|
|
|
|
def result(_binding=TOPLEVEL_BINDING)
|
|
eval @src, _binding
|
|
end
|
|
|
|
def evaluate(_context=Object.new)
|
|
if _context.is_a?(Hash)
|
|
_obj = Object.new
|
|
_context.each do |k, v| _obj.instance_variable_set("@#{k}", v) end
|
|
_context = _obj
|
|
end
|
|
_context.instance_eval @src
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module PI
|
|
end
|
|
|
|
class PI::TinyEruby
|
|
|
|
def initialize(input=nil, options={})
|
|
@escape = options[:escape] || 'Erubis::XmlHelper.escape_xml'
|
|
@src = convert(input) if input
|
|
end
|
|
|
|
attr_reader :src
|
|
|
|
EMBEDDED_PATTERN = /(^[ \t]*)?<\?rb(\s.*?)\?>([ \t]*\r?\n)?|@(!+)?\{(.*?)\}@/m
|
|
|
|
def convert(input)
|
|
src = "_buf = '';" # preamble
|
|
pos = 0
|
|
input.scan(EMBEDDED_PATTERN) do |lspace, stmt, rspace, indicator, expr|
|
|
match = Regexp.last_match
|
|
len = match.begin(0) - pos
|
|
text = input[pos, len]
|
|
pos = match.end(0)
|
|
#src << " _buf << '" << escape_text(text) << "';"
|
|
text.gsub!(/['\\]/, '\\\\\&')
|
|
src << " _buf << '" << text << "';" unless text.empty?
|
|
if stmt # <?rb ... ?>
|
|
if lspace && rspace
|
|
src << "#{lspace}#{stmt}#{rspace}"
|
|
else
|
|
src << " _buf << '" << lspace << "';" if lspace
|
|
src << stmt << ";"
|
|
src << " _buf << '" << rspace << "';" if rspace
|
|
end
|
|
else # ${...}, $!{...}
|
|
if !indicator
|
|
src << " _buf << " << @escape << "(" << expr << ");"
|
|
elsif indicator == '!'
|
|
src << " _buf << (" << expr << ").to_s;"
|
|
end
|
|
end
|
|
end
|
|
#rest = $' || input # ruby1.8
|
|
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
|
#src << " _buf << '" << escape_text(rest) << "';"
|
|
rest.gsub!(/['\\]/, '\\\\\&')
|
|
src << " _buf << '" << rest << "';" unless rest.empty?
|
|
src << "\n_buf.to_s\n" # postamble
|
|
return src
|
|
end
|
|
|
|
#def escape_text(text)
|
|
# return text.gsub!(/['\\]/, '\\\\\&') || text
|
|
#end
|
|
|
|
def result(_binding=TOPLEVEL_BINDING)
|
|
eval @src, _binding
|
|
end
|
|
|
|
def evaluate(_context=Object.new)
|
|
if _context.is_a?(Hash)
|
|
_obj = Object.new
|
|
_context.each do |k, v| _obj.instance_variable_set("@#{k}", v) end
|
|
_context = _obj
|
|
end
|
|
_context.instance_eval @src
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/tiny'
|
|
#--begin of require 'erubis/engine/enhanced'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/enhancer'
|
|
#--already included require 'erubis/engine/eruby'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
#--
|
|
## moved to engine/ruby.rb
|
|
#class EscapedEruby < Eruby
|
|
# include EscapeEnhancer
|
|
#end
|
|
#++
|
|
|
|
|
|
#--
|
|
### (obsolete)
|
|
#class FastEruby < Eruby
|
|
# include FastEnhancer
|
|
#end
|
|
#++
|
|
|
|
|
|
class StdoutEruby < Eruby
|
|
include StdoutEnhancer
|
|
end
|
|
|
|
|
|
class PrintOutEruby < Eruby
|
|
include PrintOutEnhancer
|
|
end
|
|
|
|
|
|
class PrintEnabledEruby < Eruby
|
|
include PrintEnabledEnhancer
|
|
end
|
|
|
|
|
|
class ArrayEruby < Eruby
|
|
include ArrayEnhancer
|
|
end
|
|
|
|
|
|
class ArrayBufferEruby < Eruby
|
|
include ArrayBufferEnhancer
|
|
end
|
|
|
|
|
|
class StringBufferEruby < Eruby
|
|
include StringBufferEnhancer
|
|
end
|
|
|
|
|
|
class StringIOEruby < Eruby
|
|
include StringIOEnhancer
|
|
end
|
|
|
|
|
|
class ErboutEruby < Eruby
|
|
include ErboutEnhancer
|
|
end
|
|
|
|
|
|
class NoTextEruby < Eruby
|
|
include NoTextEnhancer
|
|
end
|
|
|
|
|
|
class NoCodeEruby < Eruby
|
|
include NoCodeEnhancer
|
|
end
|
|
|
|
|
|
class SimplifiedEruby < Eruby
|
|
include SimplifyEnhancer
|
|
end
|
|
|
|
|
|
class StdoutSimplifiedEruby < Eruby
|
|
include StdoutEnhancer
|
|
include SimplifyEnhancer
|
|
end
|
|
|
|
|
|
class PrintOutSimplifiedEruby < Eruby
|
|
include PrintOutEnhancer
|
|
include SimplifyEnhancer
|
|
end
|
|
|
|
|
|
class BiPatternEruby < Eruby
|
|
include BiPatternEnhancer
|
|
end
|
|
|
|
|
|
class PercentLineEruby < Eruby
|
|
include PercentLineEnhancer
|
|
end
|
|
|
|
|
|
class HeaderFooterEruby < Eruby
|
|
include HeaderFooterEnhancer
|
|
end
|
|
|
|
|
|
class DeleteIndentEruby < Eruby
|
|
include DeleteIndentEnhancer
|
|
end
|
|
|
|
|
|
class InterpolationEruby < Eruby
|
|
include InterpolationEnhancer
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/enhanced'
|
|
#--begin of require 'erubis/engine/optimized'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
|
|
#--already included require 'erubis/engine/eruby'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module OptimizedGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return []
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= "Erubis::XmlHelper.escape_xml"
|
|
@initialized = false
|
|
@prev_is_expr = false
|
|
end
|
|
|
|
protected
|
|
|
|
def escape_text(text)
|
|
text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\'
|
|
end
|
|
|
|
def escaped_expr(code)
|
|
@escapefunc ||= 'Erubis::XmlHelper.escape_xml'
|
|
return "#{@escapefunc}(#{code})"
|
|
end
|
|
|
|
def switch_to_expr(src)
|
|
return if @prev_is_expr
|
|
@prev_is_expr = true
|
|
src << ' _buf'
|
|
end
|
|
|
|
def switch_to_stmt(src)
|
|
return unless @prev_is_expr
|
|
@prev_is_expr = false
|
|
src << ';'
|
|
end
|
|
|
|
def add_preamble(src)
|
|
#@initialized = false
|
|
#@prev_is_expr = false
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if text.empty?
|
|
if @initialized
|
|
switch_to_expr(src)
|
|
src << " << '" << escape_text(text) << "'"
|
|
else
|
|
src << "_buf = '" << escape_text(text) << "';"
|
|
@initialized = true
|
|
end
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
switch_to_stmt(src) if @initialized
|
|
#super
|
|
src << code
|
|
src << ';' unless code[-1] == ?\n
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
unless @initialized; src << "_buf = ''"; @initialized = true; end
|
|
switch_to_expr(src)
|
|
src << " << (" << code << ").to_s"
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
unless @initialized; src << "_buf = ''"; @initialized = true; end
|
|
switch_to_expr(src)
|
|
src << " << " << escaped_expr(code)
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
code.strip!
|
|
s = (code.dump =~ /\A"(.*)"\z/) && $1
|
|
src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
|
|
end
|
|
|
|
def add_postamble(src)
|
|
#super if @initialized
|
|
src << "\n_buf\n" if @initialized
|
|
end
|
|
|
|
end # end of class OptimizedEruby
|
|
|
|
|
|
##
|
|
## Eruby class which generates optimized ruby code
|
|
##
|
|
class OptimizedEruby < Basic::Engine # Eruby
|
|
include RubyEvaluator
|
|
include OptimizedGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'rb'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## XmlEruby class which generates optimized ruby code
|
|
##
|
|
class OptimizedXmlEruby < OptimizedEruby
|
|
include EscapeEnhancer
|
|
|
|
def add_expr_debug(src, code)
|
|
switch_to_stmt(src) if indicator == '===' && !@initialized
|
|
super
|
|
end
|
|
|
|
end # end of class OptimizedXmlEruby
|
|
|
|
end
|
|
#--end of require 'erubis/engine/optimized'
|
|
#--already included require 'erubis/engine/eruby'
|
|
#--begin of require 'erubis/engine/ephp'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module PhpGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return []
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= 'htmlspecialchars'
|
|
end
|
|
|
|
def add_preamble(src)
|
|
# empty
|
|
end
|
|
|
|
def escape_text(text)
|
|
return text.gsub!(/<\?xml\b/, '<<?php ?>?xml') || text
|
|
end
|
|
|
|
def add_text(src, text)
|
|
src << escape_text(text)
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
code.strip!
|
|
src << "<?php echo #{code}; ?>"
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
add_expr_literal(src, escaped_expr(code))
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
code.strip!
|
|
s = code.gsub(/\'/, "\\'")
|
|
src << "<?php error_log('*** debug: #{s}='.(#{code}), 0); ?>"
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << "<?php"
|
|
src << " " if code[0] != ?\ #
|
|
if code[-1] == ?\n
|
|
code.chomp!
|
|
src << code << "?>\n"
|
|
else
|
|
src << code << "?>"
|
|
end
|
|
end
|
|
|
|
def add_postamble(src)
|
|
# empty
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for PHP
|
|
##
|
|
class Ephp < Basic::Engine
|
|
include PhpGenerator
|
|
end
|
|
|
|
|
|
class EscapedEphp < Ephp
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
#class XmlEphp < Ephp
|
|
# include EscapeEnhancer
|
|
#end
|
|
|
|
|
|
class PI::Ephp < PI::Engine
|
|
include PhpGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'php'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/ephp'
|
|
#--begin of require 'erubis/engine/ec'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module CGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return [
|
|
[:indent, '', "indent spaces (ex. ' ')"],
|
|
[:out, 'stdout', "output file pointer name"],
|
|
]
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= "escape"
|
|
@indent = properties[:indent] || ''
|
|
@out = properties[:out] || 'stdout'
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "#line 1 \"#{self.filename}\"\n" if self.filename
|
|
end
|
|
|
|
def escape_text(text)
|
|
@@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
|
|
text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] }
|
|
return text
|
|
end
|
|
|
|
def escaped_expr(code)
|
|
return "#{@escapefunc}(#{code.strip}, #{@out})"
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if text.empty?
|
|
src << (src.empty? || src[-1] == ?\n ? @indent : ' ')
|
|
src << "fputs("
|
|
i = 0
|
|
text.each_line do |line|
|
|
src << "\n" << @indent << ' ' if i > 0
|
|
i += 1
|
|
src << '"' << escape_text(line) << '"'
|
|
end
|
|
src << ", #{@out});" #<< (text[-1] == ?\n ? "\n" : "")
|
|
src << "\n" if text[-1] == ?\n
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << code
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
src << @indent if src.empty? || src[-1] == ?\n
|
|
src << " fprintf(#{@out}, " << code.strip << ');'
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
src << @indent if src.empty? || src[-1] == ?\n
|
|
src << ' ' << escaped_expr(code) << ';'
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
code.strip!
|
|
s = nil
|
|
if code =~ /\A\".*?\"\s*,\s*(.*)/
|
|
s = $1.gsub(/[%"]/, '\\\1') + '='
|
|
end
|
|
src << @indent if src.empty? || src[-1] == ?\n
|
|
src << " fprintf(stderr, \"*** debug: #{s}\" #{code});"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
# empty
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for C
|
|
##
|
|
class Ec < Basic::Engine
|
|
include CGenerator
|
|
end
|
|
|
|
|
|
class EscapedEc < Ec
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
#class XmlEc < Ec
|
|
# include EscapeEnhancer
|
|
#end
|
|
|
|
class PI::Ec < PI::Engine
|
|
include CGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'c'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/ec'
|
|
#--begin of require 'erubis/engine/ejava'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module JavaGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return [
|
|
[:indent, '', "indent spaces (ex. ' ')"],
|
|
[:buf, '_buf', "output buffer name"],
|
|
[:bufclass, 'StringBuffer', "output buffer class (ex. 'StringBuilder')"],
|
|
]
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= 'escape'
|
|
@indent = properties[:indent] || ''
|
|
@buf = properties[:buf] || '_buf'
|
|
@bufclass = properties[:bufclass] || 'StringBuffer'
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "#{@indent}#{@bufclass} #{@buf} = new #{@bufclass}();"
|
|
end
|
|
|
|
def escape_text(text)
|
|
@@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
|
|
return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if text.empty?
|
|
src << (src.empty? || src[-1] == ?\n ? @indent : ' ')
|
|
src << @buf << ".append("
|
|
i = 0
|
|
text.each_line do |line|
|
|
src << "\n" << @indent << ' + ' if i > 0
|
|
i += 1
|
|
src << '"' << escape_text(line) << '"'
|
|
end
|
|
src << ");" << (text[-1] == ?\n ? "\n" : "")
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << code
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
src << @indent if src.empty? || src[-1] == ?\n
|
|
code.strip!
|
|
src << " #{@buf}.append(#{code});"
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
add_expr_literal(src, escaped_expr(code))
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
code.strip!
|
|
src << @indent if src.empty? || src[-1] == ?\n
|
|
src << " System.err.println(\"*** debug: #{code}=\"+(#{code}));"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" if src[-1] == ?;
|
|
src << @indent << "return " << @buf << ".toString();\n"
|
|
#src << @indent << "System.out.print(" << @buf << ".toString());\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for Java
|
|
##
|
|
class Ejava < Basic::Engine
|
|
include JavaGenerator
|
|
end
|
|
|
|
|
|
class EscapedEjava < Ejava
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
#class XmlEjava < Ejava
|
|
# include EscapeEnhancer
|
|
#end
|
|
|
|
class PI::Ejava < PI::Engine
|
|
include JavaGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'java'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
#--end of require 'erubis/engine/ejava'
|
|
#--begin of require 'erubis/engine/escheme'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module SchemeGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return [
|
|
[:func, '_add', "function name (ex. 'display')"],
|
|
]
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= 'escape'
|
|
@func = properties[:func] || '_add' # or 'display'
|
|
end
|
|
|
|
def add_preamble(src)
|
|
return unless @func == '_add'
|
|
src << "(let ((_buf '())) " + \
|
|
"(define (_add x) (set! _buf (cons x _buf))) "
|
|
#src << "(let* ((_buf '())" + \
|
|
# " (_add (lambda (x) (set! _buf (cons x _buf))))) "
|
|
end
|
|
|
|
def escape_text(text)
|
|
@table_ ||= { '"'=>'\\"', '\\'=>'\\\\' }
|
|
text.gsub!(/["\\]/) { |m| @table_[m] }
|
|
return text
|
|
end
|
|
|
|
def escaped_expr(code)
|
|
code.strip!
|
|
return "(#{@escapefunc} #{code})"
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if text.empty?
|
|
t = escape_text(text)
|
|
if t[-1] == ?\n
|
|
t[-1, 1] = ''
|
|
src << "(#{@func} \"" << t << "\\n\")\n"
|
|
else
|
|
src << "(#{@func} \"" << t << '")'
|
|
end
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << code
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
code.strip!
|
|
src << "(#{@func} #{code})"
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
add_expr_literal(src, escaped_expr(code))
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
s = (code.strip! || code).gsub(/\"/, '\\"')
|
|
src << "(display \"*** debug: #{s}=\")(display #{code.strip})(display \"\\n\")"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
return unless @func == '_add'
|
|
src << "\n" unless src[-1] == ?\n
|
|
src << " (reverse _buf))\n"
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for Scheme
|
|
##
|
|
class Escheme < Basic::Engine
|
|
include SchemeGenerator
|
|
end
|
|
|
|
|
|
class EscapedEscheme < Escheme
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
#class XmlEscheme < Escheme
|
|
# include EscapeEnhancer
|
|
#end
|
|
|
|
|
|
class PI::Escheme < PI::Engine
|
|
include SchemeGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'scheme'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/escheme'
|
|
#--begin of require 'erubis/engine/eperl'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module PerlGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
return [
|
|
[:func, 'print', "function name"],
|
|
]
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= 'encode_entities'
|
|
@func = properties[:func] || 'print'
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "use HTML::Entities; ";
|
|
end
|
|
|
|
def escape_text(text)
|
|
return text.gsub!(/['\\]/, '\\\\\&') || text
|
|
end
|
|
|
|
def add_text(src, text)
|
|
src << @func << "('" << escape_text(text) << "'); " unless text.empty?
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
code.strip!
|
|
src << @func << "(" << code << "); "
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
add_expr_literal(src, escaped_expr(code))
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
code.strip!
|
|
s = code.gsub(/\'/, "\\'")
|
|
src << @func << "('*** debug: #{code}=', #{code}, \"\\n\");"
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << code
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" unless src[-1] == ?\n
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for Perl
|
|
##
|
|
class Eperl < Basic::Engine
|
|
include PerlGenerator
|
|
end
|
|
|
|
|
|
class EscapedEperl < Eperl
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
#class XmlEperl < Eperl
|
|
# include EscapeEnhancer
|
|
#end
|
|
|
|
|
|
class PI::Eperl < PI::Engine
|
|
include PerlGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'perl'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/eperl'
|
|
#--begin of require 'erubis/engine/ejavascript'
|
|
##
|
|
## $Release: 2.6.5 $
|
|
## copyright(c) 2006-2009 kuwata-lab.com all rights reserved.
|
|
##
|
|
|
|
#--already included require 'erubis/engine'
|
|
#--already included require 'erubis/enhancer'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
module JavascriptGenerator
|
|
include Generator
|
|
|
|
def self.supported_properties() # :nodoc:
|
|
list = []
|
|
#list << [:indent, '', "indent spaces (ex. ' ')"]
|
|
#list << [:buf, '_buf', "output buffer name"]
|
|
list << [:docwrite, true, "use 'document.write()' when true"]
|
|
return list
|
|
end
|
|
|
|
def init_generator(properties={})
|
|
super
|
|
@escapefunc ||= 'escape'
|
|
@indent = properties[:indent] || ''
|
|
@buf = properties[:out] || '_buf'
|
|
@docwrite = properties[:docwrite] != false # '!= false' will be removed in the next release
|
|
end
|
|
|
|
def add_preamble(src)
|
|
src << "#{@indent}var #{@buf} = [];"
|
|
end
|
|
|
|
def escape_text(text)
|
|
@@table_ ||= { "\r"=>"\\r", "\n"=>"\\n\\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
|
|
return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text
|
|
end
|
|
|
|
def add_indent(src, indent)
|
|
src << (src.empty? || src[-1] == ?\n ? indent : ' ')
|
|
end
|
|
|
|
def add_text(src, text)
|
|
return if text.empty?
|
|
add_indent(src, @indent)
|
|
src << @buf << '.push("'
|
|
s = escape_text(text)
|
|
if s[-1] == ?\n
|
|
s[-2, 2] = ''
|
|
src << s << "\");\n"
|
|
else
|
|
src << s << "\");"
|
|
end
|
|
end
|
|
|
|
def add_stmt(src, code)
|
|
src << code
|
|
end
|
|
|
|
def add_expr_literal(src, code)
|
|
add_indent(src, @indent)
|
|
code.strip!
|
|
src << "#{@buf}.push(#{code});"
|
|
end
|
|
|
|
def add_expr_escaped(src, code)
|
|
add_expr_literal(src, escaped_expr(code))
|
|
end
|
|
|
|
def add_expr_debug(src, code)
|
|
add_indent(src, @indent)
|
|
code.strip!
|
|
src << "alert(\"*** debug: #{code}=\"+(#{code}));"
|
|
end
|
|
|
|
def add_postamble(src)
|
|
src << "\n" if src[-1] == ?;
|
|
if @docwrite
|
|
src << @indent << 'document.write(' << @buf << ".join(\"\"));\n"
|
|
else
|
|
src << @indent << @buf << ".join(\"\");\n"
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
##
|
|
## engine for JavaScript
|
|
##
|
|
class Ejavascript < Basic::Engine
|
|
include JavascriptGenerator
|
|
end
|
|
|
|
|
|
class EscapedEjavascript < Ejavascript
|
|
include EscapeEnhancer
|
|
end
|
|
|
|
|
|
#class XmlEjavascript < Ejavascript
|
|
# include EscapeEnhancer
|
|
#end
|
|
|
|
|
|
class PI::Ejavascript < PI::Engine
|
|
include JavascriptGenerator
|
|
|
|
def init_converter(properties={})
|
|
@pi = 'js'
|
|
super(properties)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
#--end of require 'erubis/engine/ejavascript'
|
|
|
|
|
|
module Erubis
|
|
|
|
|
|
Ejs = Ejavascript
|
|
EscapedEjs = EscapedEjavascript
|
|
|
|
|
|
class CommandOptionError < ErubisError
|
|
end
|
|
|
|
|
|
##
|
|
## main class of command
|
|
##
|
|
## ex.
|
|
## Main.main(ARGV)
|
|
##
|
|
class Main
|
|
|
|
def self.main(argv=ARGV)
|
|
status = 0
|
|
begin
|
|
Main.new.execute(ARGV)
|
|
rescue CommandOptionError => ex
|
|
$stderr.puts ex.message
|
|
status = 1
|
|
end
|
|
exit(status)
|
|
end
|
|
|
|
def initialize
|
|
@single_options = "hvxztTSbeBXNUC"
|
|
@arg_options = "pcrfKIlaE" #C
|
|
@option_names = {
|
|
'h' => :help,
|
|
'v' => :version,
|
|
'x' => :source,
|
|
'z' => :syntax,
|
|
'T' => :unexpand,
|
|
't' => :untabify, # obsolete
|
|
'S' => :intern,
|
|
'b' => :bodyonly,
|
|
'B' => :binding,
|
|
'p' => :pattern,
|
|
'c' => :context,
|
|
#'C' => :class,
|
|
'e' => :escape,
|
|
'r' => :requires,
|
|
'f' => :datafiles,
|
|
'K' => :kanji,
|
|
'I' => :includes,
|
|
'l' => :lang,
|
|
'a' => :action,
|
|
'E' => :enhancers,
|
|
'X' => :notext,
|
|
'N' => :linenum,
|
|
'U' => :unique,
|
|
'C' => :compact,
|
|
}
|
|
assert unless @single_options.length + @arg_options.length == @option_names.length
|
|
(@single_options + @arg_options).each_byte do |ch|
|
|
assert unless @option_names.key?(ch.chr)
|
|
end
|
|
end
|
|
|
|
|
|
def execute(argv=ARGV)
|
|
## parse command-line options
|
|
options, properties = parse_argv(argv, @single_options, @arg_options)
|
|
filenames = argv
|
|
options['h'] = true if properties[:help]
|
|
opts = Object.new
|
|
arr = @option_names.collect {|ch, name| "def #{name}; @#{name}; end\n" }
|
|
opts.instance_eval arr.join
|
|
options.each do |ch, val|
|
|
name = @option_names[ch]
|
|
opts.instance_variable_set("@#{name}", val)
|
|
end
|
|
|
|
## help, version, enhancer list
|
|
if opts.help || opts.version
|
|
puts version() if opts.version
|
|
puts usage() if opts.help
|
|
puts show_properties() if opts.help
|
|
puts show_enhancers() if opts.help
|
|
return
|
|
end
|
|
|
|
## include path
|
|
opts.includes.split(/,/).each do |path|
|
|
$: << path
|
|
end if opts.includes
|
|
|
|
## require library
|
|
opts.requires.split(/,/).each do |library|
|
|
require library
|
|
end if opts.requires
|
|
|
|
## action
|
|
action = opts.action
|
|
action ||= 'syntax' if opts.syntax
|
|
action ||= 'convert' if opts.source || opts.notext
|
|
|
|
## lang
|
|
lang = opts.lang || 'ruby'
|
|
action ||= 'convert' if opts.lang
|
|
|
|
## class name of Eruby
|
|
#classname = opts.class
|
|
classname = nil
|
|
klass = get_classobj(classname, lang, properties[:pi])
|
|
|
|
## kanji code
|
|
$KCODE = opts.kanji if opts.kanji
|
|
|
|
## read context values from yaml file
|
|
datafiles = opts.datafiles
|
|
context = load_datafiles(datafiles, opts)
|
|
|
|
## parse context data
|
|
if opts.context
|
|
context = parse_context_data(opts.context, opts)
|
|
end
|
|
|
|
## properties for engine
|
|
properties[:escape] = true if opts.escape && !properties.key?(:escape)
|
|
properties[:pattern] = opts.pattern if opts.pattern
|
|
#properties[:trim] = false if opts.notrim
|
|
properties[:preamble] = properties[:postamble] = false if opts.bodyonly
|
|
properties[:pi] = nil if properties[:pi] == true
|
|
|
|
## create engine and extend enhancers
|
|
engine = klass.new(nil, properties)
|
|
enhancers = get_enhancers(opts.enhancers)
|
|
#enhancers.push(Erubis::EscapeEnhancer) if opts.escape
|
|
enhancers.each do |enhancer|
|
|
engine.extend(enhancer)
|
|
engine.bipattern = properties[:bipattern] if enhancer == Erubis::BiPatternEnhancer
|
|
end
|
|
|
|
## no-text
|
|
engine.extend(Erubis::NoTextEnhancer) if opts.notext
|
|
|
|
## convert and execute
|
|
val = nil
|
|
msg = "Syntax OK\n"
|
|
if filenames && !filenames.empty?
|
|
filenames.each do |filename|
|
|
File.file?(filename) or
|
|
raise CommandOptionError.new("#{filename}: file not found.")
|
|
engine.filename = filename
|
|
engine.convert!(File.read(filename))
|
|
val = do_action(action, engine, context, filename, opts)
|
|
msg = nil if val
|
|
end
|
|
else
|
|
engine.filename = filename = '(stdin)'
|
|
engine.convert!($stdin.read())
|
|
val = do_action(action, engine, context, filename, opts)
|
|
msg = nil if val
|
|
end
|
|
print msg if action == 'syntax' && msg
|
|
|
|
end
|
|
|
|
private
|
|
|
|
def do_action(action, engine, context, filename, opts)
|
|
case action
|
|
when 'convert'
|
|
s = manipulate_src(engine.src, opts)
|
|
when nil, 'exec', 'execute'
|
|
s = opts.binding ? engine.result(context.to_hash) : engine.evaluate(context)
|
|
when 'syntax'
|
|
s = check_syntax(filename, engine.src)
|
|
else
|
|
raise "*** internal error"
|
|
end
|
|
print s if s
|
|
return s
|
|
end
|
|
|
|
def manipulate_src(source, opts)
|
|
flag_linenum = opts.linenum
|
|
flag_unique = opts.unique
|
|
flag_compact = opts.compact
|
|
if flag_linenum
|
|
n = 0
|
|
source.gsub!(/^/) { n += 1; "%5d: " % n }
|
|
source.gsub!(/^ *\d+:\s+?\n/, '') if flag_compact
|
|
source.gsub!(/(^ *\d+:\s+?\n)+/, "\n") if flag_unique
|
|
else
|
|
source.gsub!(/^\s*?\n/, '') if flag_compact
|
|
source.gsub!(/(^\s*?\n)+/, "\n") if flag_unique
|
|
end
|
|
return source
|
|
end
|
|
|
|
def usage(command=nil)
|
|
command ||= File.basename($0)
|
|
buf = []
|
|
buf << "erubis - embedded program converter for multi-language"
|
|
buf << "Usage: #{command} [..options..] [file ...]"
|
|
buf << " -h, --help : help"
|
|
buf << " -v : version"
|
|
buf << " -x : show converted code"
|
|
buf << " -X : show converted code, only ruby code and no text part"
|
|
buf << " -N : numbering: add line numbers (for '-x/-X')"
|
|
buf << " -U : unique: compress empty lines to a line (for '-x/-X')"
|
|
buf << " -C : compact: remove empty lines (for '-x/-X')"
|
|
buf << " -b : body only: no preamble nor postamble (for '-x/-X')"
|
|
buf << " -z : syntax checking"
|
|
buf << " -e : escape (equal to '--E Escape')"
|
|
buf << " -p pattern : embedded pattern (default '<% %>')"
|
|
buf << " -l lang : convert but no execute (ruby/php/c/java/scheme/perl/js)"
|
|
buf << " -E e1,e2,... : enhancer names (Escape, PercentLine, BiPattern, ...)"
|
|
buf << " -I path : library include path"
|
|
buf << " -K kanji : kanji code (euc/sjis/utf8) (default none)"
|
|
buf << " -c context : context data string (yaml inline style or ruby code)"
|
|
buf << " -f datafile : context data file ('*.yaml', '*.yml', or '*.rb')"
|
|
#buf << " -t : expand tab characters in YAML file"
|
|
buf << " -T : don't expand tab characters in YAML file"
|
|
buf << " -S : convert mapping key from string to symbol in YAML file"
|
|
buf << " -B : invoke 'result(binding)' instead of 'evaluate(context)'"
|
|
buf << " --pi=name : parse '<?name ... ?>' instead of '<% ... %>'"
|
|
#'
|
|
# -T : don't trim spaces around '<% %>'
|
|
# -c class : class name (XmlEruby/PercentLineEruby/...) (default Eruby)
|
|
# -r library : require library
|
|
# -a : action (convert/execute)
|
|
return buf.join("\n")
|
|
end
|
|
|
|
def collect_supported_properties(erubis_klass)
|
|
list = []
|
|
erubis_klass.ancestors.each do |klass|
|
|
if klass.respond_to?(:supported_properties)
|
|
list.concat(klass.supported_properties)
|
|
end
|
|
end
|
|
return list
|
|
end
|
|
|
|
def show_properties
|
|
s = "supported properties:\n"
|
|
basic_props = collect_supported_properties(Erubis::Basic::Engine)
|
|
pi_props = collect_supported_properties(Erubis::PI::Engine)
|
|
list = []
|
|
common_props = basic_props & pi_props
|
|
list << ['(common)', common_props]
|
|
list << ['(basic)', basic_props - common_props]
|
|
list << ['(pi)', pi_props - common_props]
|
|
%w[ruby php c java scheme perl javascript].each do |lang|
|
|
klass = Erubis.const_get("E#{lang}")
|
|
list << [lang, collect_supported_properties(klass) - basic_props]
|
|
end
|
|
list.each do |lang, props|
|
|
s << " * #{lang}\n"
|
|
props.each do |name, default_val, desc|
|
|
s << (" --%-23s : %s\n" % ["#{name}=#{default_val.inspect}", desc])
|
|
end
|
|
end
|
|
s << "\n"
|
|
return s
|
|
end
|
|
|
|
def show_enhancers
|
|
dict = {}
|
|
ObjectSpace.each_object(Module) do |mod|
|
|
dict[$1] = mod if mod.name =~ /\AErubis::(.*)Enhancer\z/
|
|
end
|
|
s = "enhancers:\n"
|
|
dict.sort_by {|name, mod| name }.each do |name, mod|
|
|
s << (" %-13s : %s\n" % [name, mod.desc])
|
|
end
|
|
return s
|
|
end
|
|
|
|
def version
|
|
return Erubis::VERSION
|
|
end
|
|
|
|
def parse_argv(argv, arg_none='', arg_required='', arg_optional='')
|
|
options = {}
|
|
context = {}
|
|
while argv[0] && argv[0][0] == ?-
|
|
optstr = argv.shift
|
|
optstr = optstr[1, optstr.length-1]
|
|
#
|
|
if optstr[0] == ?- # context
|
|
optstr =~ /\A\-([-\w]+)(?:=(.*))?/ or
|
|
raise CommandOptionError.new("-#{optstr}: invalid context value.")
|
|
name, value = $1, $2
|
|
name = name.gsub(/-/, '_').intern
|
|
#value = value.nil? ? true : YAML.load(value) # error, why?
|
|
value = value.nil? ? true : YAML.load("---\n#{value}\n")
|
|
context[name] = value
|
|
#
|
|
else # options
|
|
while optstr && !optstr.empty?
|
|
optchar = optstr[0].chr
|
|
optstr = optstr[1..-1]
|
|
if arg_none.include?(optchar)
|
|
options[optchar] = true
|
|
elsif arg_required.include?(optchar)
|
|
arg = optstr.empty? ? argv.shift : optstr or
|
|
raise CommandOptionError.new("-#{optchar}: #{@option_names[optchar]} required.")
|
|
options[optchar] = arg
|
|
optstr = nil
|
|
elsif arg_optional.include?(optchar)
|
|
arg = optstr.empty? ? true : optstr
|
|
options[optchar] = arg
|
|
optstr = nil
|
|
else
|
|
raise CommandOptionError.new("-#{optchar}: unknown option.")
|
|
end
|
|
end
|
|
end
|
|
#
|
|
end # end of while
|
|
|
|
return options, context
|
|
end
|
|
|
|
|
|
def untabify(str, width=8)
|
|
list = str.split(/\t/)
|
|
last = list.pop
|
|
sb = ''
|
|
list.each do |s|
|
|
column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
|
|
n = width - (column % width)
|
|
sb << s << (' ' * n)
|
|
end
|
|
sb << last
|
|
return sb
|
|
end
|
|
#--
|
|
#def untabify(str, width=8)
|
|
# sb = ''
|
|
# str.scan(/(.*?)\t/m) do |s, |
|
|
# len = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
|
|
# sb << s << (" " * (width - len % width))
|
|
# end
|
|
# return $' ? (sb << $') : str
|
|
#end
|
|
#++
|
|
|
|
|
|
def get_classobj(classname, lang, pi)
|
|
classname ||= "E#{lang}"
|
|
base_module = pi ? Erubis::PI : Erubis
|
|
begin
|
|
klass = base_module.const_get(classname)
|
|
rescue NameError
|
|
klass = nil
|
|
end
|
|
unless klass
|
|
if lang
|
|
msg = "-l #{lang}: invalid language name (class #{base_module.name}::#{classname} not found)."
|
|
else
|
|
msg = "-c #{classname}: invalid class name."
|
|
end
|
|
raise CommandOptionError.new(msg)
|
|
end
|
|
return klass
|
|
end
|
|
|
|
def get_enhancers(enhancer_names)
|
|
return [] unless enhancer_names
|
|
enhancers = []
|
|
shortname = nil
|
|
begin
|
|
enhancer_names.split(/,/).each do |name|
|
|
shortname = name
|
|
enhancers << Erubis.const_get("#{shortname}Enhancer")
|
|
end
|
|
rescue NameError
|
|
raise CommandOptionError.new("#{shortname}: no such Enhancer (try '-h' to show all enhancers).")
|
|
end
|
|
return enhancers
|
|
end
|
|
|
|
def load_datafiles(filenames, opts)
|
|
context = Erubis::Context.new
|
|
return context unless filenames
|
|
filenames.split(/,/).each do |filename|
|
|
filename.strip!
|
|
test(?f, filename) or raise CommandOptionError.new("#{filename}: file not found.")
|
|
if filename =~ /\.ya?ml$/
|
|
if opts.unexpand
|
|
ydoc = YAML.load_file(filename)
|
|
else
|
|
ydoc = YAML.load(untabify(File.read(filename)))
|
|
end
|
|
ydoc.is_a?(Hash) or raise CommandOptionError.new("#{filename}: root object is not a mapping.")
|
|
intern_hash_keys(ydoc) if opts.intern
|
|
context.update(ydoc)
|
|
elsif filename =~ /\.rb$/
|
|
str = File.read(filename)
|
|
context2 = Erubis::Context.new
|
|
_instance_eval(context2, str)
|
|
context.update(context2)
|
|
else
|
|
CommandOptionError.new("#{filename}: '*.yaml', '*.yml', or '*.rb' required.")
|
|
end
|
|
end
|
|
return context
|
|
end
|
|
|
|
def _instance_eval(_context, _str)
|
|
_context.instance_eval(_str)
|
|
end
|
|
|
|
def parse_context_data(context_str, opts)
|
|
if context_str[0] == ?{
|
|
require 'yaml'
|
|
ydoc = YAML.load(context_str)
|
|
unless ydoc.is_a?(Hash)
|
|
raise CommandOptionError.new("-c: root object is not a mapping.")
|
|
end
|
|
intern_hash_keys(ydoc) if opts.intern
|
|
return ydoc
|
|
else
|
|
context = Erubis::Context.new
|
|
context.instance_eval(context_str, '-c')
|
|
return context
|
|
end
|
|
end
|
|
|
|
def intern_hash_keys(obj, done={})
|
|
return if done.key?(obj.__id__)
|
|
case obj
|
|
when Hash
|
|
done[obj.__id__] = obj
|
|
obj.keys.each do |key|
|
|
obj[key.intern] = obj.delete(key) if key.is_a?(String)
|
|
end
|
|
obj.values.each do |val|
|
|
intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array)
|
|
end
|
|
when Array
|
|
done[obj.__id__] = obj
|
|
obj.each do |val|
|
|
intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array)
|
|
end
|
|
end
|
|
end
|
|
|
|
def check_syntax(filename, src)
|
|
require 'open3'
|
|
#command = (ENV['_'] || 'ruby') + ' -wc' # ENV['_'] stores command name
|
|
bin = ENV['_'] && File.basename(ENV['_']) =~ /^ruby/ ? ENV['_'] : 'ruby'
|
|
command = bin + ' -wc'
|
|
stdin, stdout, stderr = Open3.popen3(command)
|
|
stdin.write(src)
|
|
stdin.close
|
|
result = stdout.read()
|
|
stdout.close()
|
|
errmsg = stderr.read()
|
|
stderr.close()
|
|
return nil unless errmsg && !errmsg.empty?
|
|
errmsg =~ /\A-:(\d+): /
|
|
linenum, message = $1, $'
|
|
return "#{filename}:#{linenum}: #{message}"
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
#--end of require 'erubis/main'
|
|
|
|
Erubis::Main.main(ARGV)
|