template.rb (1504B)
1 # frozen_string_literal: true 2 3 require_relative 'util' 4 5 require 'ostruct' 6 7 module Template 8 REGEX = %r{(?://\$([^\n]*(?:\n|$))|/\*\$(.*?)\*/)} 9 10 @cache = {} 11 @template_stack = [] 12 def self.template inp, vals = nil, c = nil 13 raise ArgumentError, 'Input is nil' if inp.nil? 14 return inp unless inp =~ REGEX 15 raise ArgumentError, 'No vals' if vals.nil? && @template_stack.empty? 16 17 @cache[inp] ||= begin 18 code = +'::Kernel.proc{__tmpl_out = ::String.new;' 19 prev_pos = 0 20 21 while m = inp.match(REGEX, prev_pos) 22 cur_pos = m.begin 0 23 unless prev_pos == cur_pos 24 code << "__tmpl_out << #{inp[prev_pos...cur_pos].inspect};" 25 end 26 27 snip = m.match(1) || m.match(2) 28 if snip.start_with? '=' 29 code << "__tmpl_out << (#{snip[1..]}).to_s;" 30 else 31 code << "#{snip};" 32 end 33 34 prev_pos = cur_pos + m.match(0).size 35 end 36 37 unless prev_pos == inp.size 38 code << "__tmpl_out << #{inp[prev_pos..].inspect};" 39 end 40 41 code << '__tmpl_out}' 42 eval code 43 end 44 45 46 begin 47 if vals.nil? 48 @template_stack[-1].instance_eval &@cache[inp] 49 else 50 os_vals = OpenStruct.new vals 51 @template_stack << os_vals 52 begin 53 return os_vals.instance_eval &@cache[inp] 54 ensure 55 @template_stack.pop 56 end 57 end 58 rescue 59 Util.print_error "Invalid template #{inp.inspect}: #{$!.full_message}", c 60 return '' 61 end 62 end 63 end