instiki/vendor/plugins/erubis/contrib/erubis
2010-07-04 08:51:53 -05:00

3330 lines
78 KiB
Ruby
Executable file

#!/usr/bin/env ruby
###
### $Release: 2.6.6 $
### copyright(c) 2006-2010 kuwata-lab.com all rights reserved.
###
#--begin of require 'erubis/main'
###
### $Release: 2.6.6 $
### copyright(c) 2006-2010 kuwata-lab.com all rights reserved.
###
require 'yaml'
#--begin of require 'erubis'
##
## $Release: 2.6.6 $
## copyright(c) 2006-2010 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>
## &lt;aaa&gt;</li>
## <li>b&b
## b&amp;b</li>
## <li>"ccc"
## &quot;ccc&quot;</li>
## </ul>
##
module Erubis
VERSION = ('$Release: 2.6.6 $' =~ /([.\d]+)/) && $1
end
#--begin of require 'erubis/engine'
##
## $Release: 2.6.6 $
## copyright(c) 2006-2010 kuwata-lab.com all rights reserved.
##
#--begin of require 'erubis/generator'
##
## $Release: 2.6.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 kuwata-lab.com all rights reserved.
##
#--begin of require 'erubis/error'
##
## $Release: 2.6.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 kuwata-lab.com all rights reserved.
##
module Erubis
##
## helper for xml
##
module XmlHelper
module_function
ESCAPE_TABLE = {
'&' => '&amp;',
'<' => '&lt;',
'>' => '&gt;',
'"' => '&quot;',
"'" => '&#039;',
}
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(/\&/,'&amp;').gsub(/</,'&lt;').gsub(/>/,'&gt;').gsub(/"/,'&quot;')
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.6 $
## copyright(c) 2006-2010 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> : &lt;a&gt;
## <a> : &lt;a&gt;
## b&b : b&amp;b
## b&b : b&amp;b
## "c" : &quot;c&quot;
## "c" : &quot;c&quot;
##
## 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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.6 $
## copyright(c) 2006-2010 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)