Class | Erubis::Main |
In: |
lib/erubis/main.rb
|
Parent: | Object |
# File lib/erubis/main.rb, line 40 40: def self.main(argv=ARGV) 41: status = 0 42: begin 43: Main.new.execute(ARGV) 44: rescue CommandOptionError => ex 45: $stderr.puts ex.message 46: status = 1 47: end 48: exit(status) 49: end
# File lib/erubis/main.rb, line 51 51: def initialize 52: @single_options = "hvxztTSbeBXNUC" 53: @arg_options = "pcrfKIlaE" #C 54: @option_names = { 55: 'h' => :help, 56: 'v' => :version, 57: 'x' => :source, 58: 'z' => :syntax, 59: 'T' => :unexpand, 60: 't' => :untabify, # obsolete 61: 'S' => :intern, 62: 'b' => :bodyonly, 63: 'B' => :binding, 64: 'p' => :pattern, 65: 'c' => :context, 66: #'C' => :class, 67: 'e' => :escape, 68: 'r' => :requires, 69: 'f' => :datafiles, 70: 'K' => :kanji, 71: 'I' => :includes, 72: 'l' => :lang, 73: 'a' => :action, 74: 'E' => :enhancers, 75: 'X' => :notext, 76: 'N' => :linenum, 77: 'U' => :unique, 78: 'C' => :compact, 79: } 80: assert unless @single_options.length + @arg_options.length == @option_names.length 81: (@single_options + @arg_options).each_byte do |ch| 82: assert unless @option_names.key?(ch.chr) 83: end 84: end
# File lib/erubis/main.rb, line 87 87: def execute(argv=ARGV) 88: ## parse command-line options 89: options, properties = parse_argv(argv, @single_options, @arg_options) 90: filenames = argv 91: options['h'] = true if properties[:help] 92: opts = Object.new 93: arr = @option_names.collect { |ch, name| "def #{name}; @#{name}; end\n" } 94: opts.instance_eval arr.join 95: options.each do |ch, val| 96: name = @option_names[ch] 97: opts.instance_variable_set("@#{name}", val) 98: end 99: 100: ## help, version, enhancer list 101: if opts.help || opts.version 102: puts version() if opts.version 103: puts usage() if opts.help 104: puts show_properties() if opts.help 105: puts show_enhancers() if opts.help 106: return 107: end 108: 109: ## include path 110: opts.includes.split(/,/).each do |path| 111: $: << path 112: end if opts.includes 113: 114: ## require library 115: opts.requires.split(/,/).each do |library| 116: require library 117: end if opts.requires 118: 119: ## action 120: action = opts.action 121: action ||= 'syntax' if opts.syntax 122: action ||= 'convert' if opts.source || opts.notext 123: 124: ## lang 125: lang = opts.lang || 'ruby' 126: action ||= 'convert' if opts.lang 127: 128: ## class name of Eruby 129: #classname = opts.class 130: classname = nil 131: klass = get_classobj(classname, lang, properties[:pi]) 132: 133: ## kanji code 134: $KCODE = opts.kanji if opts.kanji 135: 136: ## read context values from yaml file 137: datafiles = opts.datafiles 138: context = load_datafiles(datafiles, opts) 139: 140: ## parse context data 141: if opts.context 142: context = parse_context_data(opts.context, opts) 143: end 144: 145: ## properties for engine 146: properties[:escape] = true if opts.escape && !properties.key?(:escape) 147: properties[:pattern] = opts.pattern if opts.pattern 148: #properties[:trim] = false if opts.notrim 149: properties[:preamble] = properties[:postamble] = false if opts.bodyonly 150: properties[:pi] = nil if properties[:pi] == true 151: 152: ## create engine and extend enhancers 153: engine = klass.new(nil, properties) 154: enhancers = get_enhancers(opts.enhancers) 155: #enhancers.push(Erubis::EscapeEnhancer) if opts.escape 156: enhancers.each do |enhancer| 157: engine.extend(enhancer) 158: engine.bipattern = properties[:bipattern] if enhancer == Erubis::BiPatternEnhancer 159: end 160: 161: ## no-text 162: engine.extend(Erubis::NoTextEnhancer) if opts.notext 163: 164: ## convert and execute 165: val = nil 166: msg = "Syntax OK\n" 167: if filenames && !filenames.empty? 168: filenames.each do |filename| 169: test(?f, filename) or raise CommandOptionError.new("#{filename}: file not found.") 170: engine.filename = filename 171: engine.convert!(File.read(filename)) 172: val = do_action(action, engine, context, filename, opts) 173: msg = nil if val 174: end 175: else 176: engine.filename = filename = '(stdin)' 177: engine.convert!($stdin.read()) 178: val = do_action(action, engine, context, filename, opts) 179: msg = nil if val 180: end 181: print msg if action == 'syntax' && msg 182: 183: end
# File lib/erubis/main.rb, line 434 434: def _instance_eval(_context, _str) 435: _context.instance_eval(_str) 436: end
# File lib/erubis/main.rb, line 473 473: def check_syntax(filename, src) 474: require 'open3' 475: command = (ENV['_'] || 'ruby') + ' -wc' # ENV['_'] stores command name 476: stdin, stdout, stderr = Open3.popen3(command) 477: stdin.write(src) 478: stdin.close 479: result = stdout.read() 480: stdout.close() 481: errmsg = stderr.read() 482: stderr.close() 483: return nil unless errmsg && !errmsg.empty? 484: errmsg =~ /\A-:(\d+): / 485: linenum, message = $1, $' 486: return "#{filename}:#{linenum}: #{message}" 487: end
# File lib/erubis/main.rb, line 253 253: def collect_supported_properties(erubis_klass) 254: list = [] 255: erubis_klass.ancestors.each do |klass| 256: if klass.respond_to?(:supported_properties) 257: list.concat(klass.supported_properties) 258: end 259: end 260: return list 261: end
# File lib/erubis/main.rb, line 187 187: def do_action(action, engine, context, filename, opts) 188: case action 189: when 'convert' 190: s = manipulate_src(engine.src, opts) 191: when nil, 'exec', 'execute' 192: s = opts.binding ? engine.result(context.to_hash) : engine.evaluate(context) 193: when 'syntax' 194: s = check_syntax(filename, engine.src) 195: else 196: raise "*** internal error" 197: end 198: print s if s 199: return s 200: end
# File lib/erubis/main.rb, line 374 374: def get_classobj(classname, lang, pi) 375: classname ||= "E#{lang}" 376: base_module = pi ? Erubis::PI : Erubis 377: begin 378: klass = base_module.const_get(classname) 379: rescue NameError 380: klass = nil 381: end 382: unless klass 383: if lang 384: msg = "-l #{lang}: invalid language name (class #{base_module.name}::#{classname} not found)." 385: else 386: msg = "-c #{classname}: invalid class name." 387: end 388: raise CommandOptionError.new(msg) 389: end 390: return klass 391: end
# File lib/erubis/main.rb, line 393 393: def get_enhancers(enhancer_names) 394: return [] unless enhancer_names 395: enhancers = [] 396: shortname = nil 397: begin 398: enhancer_names.split(/,/).each do |shortname| 399: enhancers << Erubis.const_get("#{shortname}Enhancer") 400: end 401: rescue NameError 402: raise CommandOptionError.new("#{shortname}: no such Enhancer (try '-E' to show all enhancers).") 403: end 404: return enhancers 405: end
# File lib/erubis/main.rb, line 454 454: def intern_hash_keys(obj, done={}) 455: return if done.key?(obj.__id__) 456: case obj 457: when Hash 458: done[obj.__id__] = obj 459: obj.keys.each do |key| 460: obj[key.intern] = obj.delete(key) if key.is_a?(String) 461: end 462: obj.values.each do |val| 463: intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array) 464: end 465: when Array 466: done[obj.__id__] = obj 467: obj.each do |val| 468: intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array) 469: end 470: end 471: end
# File lib/erubis/main.rb, line 407 407: def load_datafiles(filenames, opts) 408: context = Erubis::Context.new 409: return context unless filenames 410: filenames.split(/,/).each do |filename| 411: filename.strip! 412: test(?f, filename) or raise CommandOptionError.new("#{filename}: file not found.") 413: if filename =~ /\.ya?ml$/ 414: if opts.unexpand 415: ydoc = YAML.load_file(filename) 416: else 417: ydoc = YAML.load(untabify(File.read(filename))) 418: end 419: ydoc.is_a?(Hash) or raise CommandOptionError.new("#{filename}: root object is not a mapping.") 420: intern_hash_keys(ydoc) if opts.intern 421: context.update(ydoc) 422: elsif filename =~ /\.rb$/ 423: str = File.read(filename) 424: context2 = Erubis::Context.new 425: _instance_eval(context2, str) 426: context.update(context2) 427: else 428: CommandOptionError.new("#{filename}: '*.yaml', '*.yml', or '*.rb' required.") 429: end 430: end 431: return context 432: end
# File lib/erubis/main.rb, line 202 202: def manipulate_src(source, opts) 203: flag_linenum = opts.linenum 204: flag_unique = opts.unique 205: flag_compact = opts.compact 206: if flag_linenum 207: n = 0 208: source.gsub!(/^/) { n += 1; "%5d: " % n } 209: source.gsub!(/^ *\d+:\s+?\n/, '') if flag_compact 210: source.gsub!(/(^ *\d+:\s+?\n)+/, "\n") if flag_unique 211: else 212: source.gsub!(/^\s*?\n/, '') if flag_compact 213: source.gsub!(/(^\s*?\n)+/, "\n") if flag_unique 214: end 215: return source 216: end
# File lib/erubis/main.rb, line 303 303: def parse_argv(argv, arg_none='', arg_required='', arg_optional='') 304: options = {} 305: context = {} 306: while argv[0] && argv[0][0] == ?- 307: optstr = argv.shift 308: optstr = optstr[1, optstr.length-1] 309: # 310: if optstr[0] == ?- # context 311: unless optstr =~ /\A\-([-\w]+)(?:=(.*))?/ 312: raise CommandOptionError.new("-#{optstr}: invalid context value.") 313: end 314: name = $1; value = $2 315: name = name.gsub(/-/, '_').intern 316: #value = value.nil? ? true : YAML.load(value) # error, why? 317: value = value.nil? ? true : YAML.load("---\n#{value}\n") 318: context[name] = value 319: # 320: else # options 321: while optstr && !optstr.empty? 322: optchar = optstr[0].chr 323: optstr[0,1] = "" 324: if arg_none.include?(optchar) 325: options[optchar] = true 326: elsif arg_required.include?(optchar) 327: arg = optstr.empty? ? argv.shift : optstr 328: unless arg 329: mesg = "-#{optchar.chr}: #{@option_args[optchar]} required." 330: raise CommandOptionError.new(mesg) 331: end 332: options[optchar] = arg 333: optstr = nil 334: elsif arg_optional.include?(optchar) 335: arg = optstr.empty? ? true : optstr 336: options[optchar] = arg 337: optstr = nil 338: else 339: raise CommandOptionError.new("-#{optchar.chr}: unknown option.") 340: end 341: end 342: end 343: # 344: end # end of while 345: 346: return options, context 347: end
# File lib/erubis/main.rb, line 438 438: def parse_context_data(context_str, opts) 439: if context_str[0] == ?{ 440: require 'yaml' 441: ydoc = YAML.load(context_str) 442: unless ydoc.is_a?(Hash) 443: raise CommandOptionError.new("-c: root object is not a mapping.") 444: end 445: intern_hash_keys(ydoc) if opts.intern 446: return ydoc 447: else 448: context = Erubis::Context.new 449: context.instance_eval(context_str, '-c') 450: return context 451: end 452: end
# File lib/erubis/main.rb, line 286 286: def show_enhancers 287: s = "enhancers:\n" 288: list = [] 289: ObjectSpace.each_object(Module) do |m| list << m end 290: list.sort_by { |m| m.name.to_s }.each do |m| 291: next unless m.name =~ /\AErubis::(.*)Enhancer\z/ 292: name = $1 293: desc = m.desc 294: s << (" %-13s : %s\n" % [name, desc]) 295: end 296: return s 297: end
# File lib/erubis/main.rb, line 263 263: def show_properties 264: s = "supported properties:\n" 265: basic_props = collect_supported_properties(Erubis::Basic::Engine) 266: pi_props = collect_supported_properties(Erubis::PI::Engine) 267: list = [] 268: common_props = basic_props & pi_props 269: list << ['(common)', common_props] 270: list << ['(basic)', basic_props - common_props] 271: list << ['(pi)', pi_props - common_props] 272: %w[ruby php c java scheme perl javascript].each do |lang| 273: klass = Erubis.const_get("E#{lang}") 274: list << [lang, collect_supported_properties(klass) - basic_props] 275: end 276: list.each do |lang, props| 277: s << " * #{lang}\n" 278: props.each do |name, default_val, desc| 279: s << (" --%-23s : %s\n" % ["#{name}=#{default_val.inspect}", desc]) 280: end 281: end 282: s << "\n" 283: return s 284: end
# File lib/erubis/main.rb, line 350 350: def untabify(str, width=8) 351: list = str.split(/\t/) 352: last = list.pop 353: sb = '' 354: list.each do |s| 355: column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length 356: n = width - (column % width) 357: sb << s << (' ' * n) 358: end 359: sb << last 360: return sb 361: end
# File lib/erubis/main.rb, line 218 218: def usage(command=nil) 219: command ||= File.basename($0) 220: buf = [] 221: buf << "erubis - embedded program converter for multi-language" 222: buf << "Usage: #{command} [..options..] [file ...]" 223: buf << " -h, --help : help" 224: buf << " -v : version" 225: buf << " -x : show converted code" 226: buf << " -X : show converted code, only ruby code and no text part" 227: buf << " -N : numbering: add line numbers (for '-x/-X')" 228: buf << " -U : unique: compress empty lines to a line (for '-x/-X')" 229: buf << " -C : compact: remove empty lines (for '-x/-X')" 230: buf << " -b : body only: no preamble nor postamble (for '-x/-X')" 231: buf << " -z : syntax checking" 232: buf << " -e : escape (equal to '--E Escape')" 233: buf << " -p pattern : embedded pattern (default '<% %>')" 234: buf << " -l lang : convert but no execute (ruby/php/c/java/scheme/perl/js)" 235: buf << " -E e1,e2,... : enhancer names (Escape, PercentLine, BiPattern, ...)" 236: buf << " -I path : library include path" 237: buf << " -K kanji : kanji code (euc/sjis/utf8) (default none)" 238: buf << " -c context : context data string (yaml inline style or ruby code)" 239: buf << " -f datafile : context data file ('*.yaml', '*.yml', or '*.rb')" 240: #buf << " -t : expand tab characters in YAML file" 241: buf << " -T : don't expand tab characters in YAML file" 242: buf << " -S : convert mapping key from string to symbol in YAML file" 243: buf << " -B : invoke 'result(binding)' instead of 'evaluate(context)'" 244: buf << " --pi=name : parse '<?name ... ?>' instead of '<% ... %>'" 245: #' 246: # -T : don't trim spaces around '<% %>' 247: # -c class : class name (XmlEruby/PercentLineEruby/...) (default Eruby) 248: # -r library : require library 249: # -a : action (convert/execute) 250: return buf.join("\n") 251: end