Class | Tangerine |
In: |
lib/nukumi2/page.rb
lib/nukumi2/vendor/tangerine.rb |
Parent: | Object |
Tangerine — A general-purpose templating system Copyright (C) 2004 Christian Neukirchen <purl.org/net/chneukirchen>
Licensed under the same terms as Ruby itself.
VERSION | = | "0.3.1" | Version information |
code | [R] | The generated code. |
taglib | [RW] | The tag library being used. |
Indent the string to depth, only subsequent lines.
# File lib/nukumi2/vendor/tangerine.rb, line 19 19: def self.indent(depth, string) 20: string.gsub(/\n^/, "\n" + ' '*depth) 21: end
Create, parse and compile a new Tangerine template given in template.
# File lib/nukumi2/vendor/tangerine.rb, line 24 24: def initialize(template, taglib=nil, indent=0) 25: super template 26: 27: @taglib = taglib 28: @indent = indent 29: 30: @uid = (@@uid += 1) 31: @code = "" 32: 33: generate_header 34: generate_code 35: generate_footer 36: compile 37: end
Compile generated code into a Proc.
# File lib/nukumi2/vendor/tangerine.rb, line 173 173: def compile 174: begin 175: @template = eval @code, nil, "(tangerine)", 1 176: rescue SyntaxError 177: raise 178: "Internal error during compilation. Please submit a bugreport.\n" << $! 179: end 180: end
# File lib/nukumi2/page.rb, line 5 5: def content_type 6: @taglib.resolve("content-type").strip 7: end
Return a string to be appended to string in order to return code that performs output escaping.
# File lib/nukumi2/vendor/tangerine.rb, line 41 41: def escaper 42: "" 43: end
Expand the template for object by calling given methods and inline code. Output gets sent to output using <<. If called with only one element, a string holding the result is returned.
# File lib/nukumi2/vendor/tangerine.rb, line 48 48: def expand(output, object=nil) 49: if object.nil? 50: object = output 51: output = "" 52: end 53: 54: begin 55: @template.call output, object 56: rescue => exception 57: raise exception.exception("During template expansion:\n" << 58: " #{exception}\n" << 59: " #{Tangerine.indent 4, exception.backtrace.join("\n")}\n") 60: end 61: end
Parse the template and generate appropriate output code.
# File lib/nukumi2/vendor/tangerine.rb, line 74 74: def generate_code 75: @cursor = 0 76: @mark = 0 77: 78: while @cursor = index(',,', @cursor) 79: # Skip second comma. 80: skip 81: 82: # Do comma escaping? 83: if peek == ?, 84: skip while peek && peek == ?, 85: emit_text selection 86: @mark = @cursor 87: next 88: else 89: emit_text selection 90: end 91: 92: # Skip comment? 93: if peek == ?# 94: @mark = @cursor = index("\n", @cursor) + 1 95: emit_code "\n" # Keep lineno uptodate. 96: next 97: end 98: 99: # Initialize local variables. 100: method = "" 101: resolve = false 102: indent = get_indentation 103: escape = get_escape 104: ignore_result = get_ignore_result 105: 106: case peek 107: when ?@ 108: # Use taglib ... 109: skip 110: resolve = true 111: tag = get_tag 112: raise 'Tag not resolvable: foo' unless @taglib 113: expansion = @taglib.resolve(tag) 114: when ?( 115: # ... or instance_eval ... 116: skip 117: method = read_to '('[0], ')'[0] 118: when ?[ 119: # Use instance_eval + refhack 120: skip 121: method = "self" + read_to('['[0], ']'[0]) 122: else 123: # ... or send? 124: method = get_tag 125: end 126: 127: # Read block? 128: block = read_block 129: 130: skip 131: 132: if indent == 0 133: if @indent != 0 134: i = "Tangerine.indent(#{@indent}, " 135: else 136: i = "(" 137: end 138: else 139: i = "Tangerine.indent(#{indent}, " 140: end 141: 142: if resolve 143: if block 144: # + 1 for | 145: emit_code "#{result_variable} = #{i}" + self.class.new(expansion, @taglib, indent+1).code + ".call('', #{object_variable})); "; 146: else 147: emit_code self.class.new(expansion, @taglib, indent).code + ".call(#{output_variable}, #{object_variable}); "; 148: end 149: elsif method =~ /^[\w_]+[!?]?$/ 150: emit_code "#{result_variable} = #{object_variable}.#{method}; " 151: else 152: emit_code "#{result_variable} = #{object_variable}.instance_eval(#{method.dump}); " 153: end 154: 155: unless ignore_result 156: if block 157: emit_code "#{template_variable} = " + self.class.new(block, @taglib, indent).code 158: emit_code "; ((#{result_variable} && [*(#{result_variable} == true ? [#{object_variable}] : #{result_variable})].each { |d| #{template_variable}.call(#{output_variable}, d)}).to_s#{escape}); " 159: elsif not resolve 160: emit_code "#{output_variable} << #{i}#{result_variable}.to_s#{escape}); " 161: end 162: end 163: 164: @mark = @cursor 165: end 166: 167: @cursor = -1 if @cursor.nil? 168: 169: emit_text selection_to_end 170: end
Emit the code footer.
# File lib/nukumi2/vendor/tangerine.rb, line 69 69: def generate_footer 70: emit_code "#{output_variable} }" 71: end
Emit the code header.
# File lib/nukumi2/vendor/tangerine.rb, line 64 64: def generate_header 65: emit_code "lambda { |#{output_variable}, #{object_variable}| " 66: end
Generate code c.
# File lib/nukumi2/vendor/tangerine.rb, line 280 280: def emit_code(c) 281: @code << c 282: end
Generate output code to emit t.
# File lib/nukumi2/vendor/tangerine.rb, line 285 285: def emit_text(t) 286: emit_code "#{output_variable} << #{t.dump}; " 287: emit_code "\n" * t.count("\n") 288: end
# File lib/nukumi2/vendor/tangerine.rb, line 244 244: def get_escape 245: # Disable escaping? 246: if peek == ?! 247: skip 248: "" 249: else 250: escaper 251: end 252: end
# File lib/nukumi2/vendor/tangerine.rb, line 254 254: def get_ignore_result 255: if peek == ?: 256: skip 257: true 258: else 259: false 260: end 261: end
# File lib/nukumi2/vendor/tangerine.rb, line 234 234: def get_indentation 235: # Indent like this line? 236: if peek == ?| 237: skip 238: @cursor - (self.rindex("\n", @cursor) || 0) - 3 239: else 240: 0 241: end 242: end
# File lib/nukumi2/vendor/tangerine.rb, line 263 263: def get_tag 264: tag = "" 265: while peek && peek.chr =~ /[\w0-9_?!.]/ 266: tag << getc 267: end 268: 269: # Allow dots, but skip them if last char. 270: if tag[-1] == ?. 271: backskip 272: tag[0..-2] 273: else 274: tag 275: end 276: end
# File lib/nukumi2/vendor/tangerine.rb, line 212 212: def getc 213: @cursor += 1 214: self[@cursor] 215: end
# File lib/nukumi2/vendor/tangerine.rb, line 225 225: def read_block 226: if peek == ?{ 227: skip 228: read_to('{'[0], '}'[0])[1...-1].gsub(/\A\n/, '') 229: else 230: nil 231: end 232: end
Read from the current position to close, ignoring nested open/close.
# File lib/nukumi2/vendor/tangerine.rb, line 292 292: def read_to(open, close) 293: mark = @cursor 294: 295: open = open.chr 296: close = close.chr 297: 298: while @cursor = index(close, @cursor+1) 299: part = self[mark..@cursor] 300: 301: return part if part.count(open) == part.count(close) 302: end 303: 304: raise RuntimeError, "Hit end of template looking for matching `#{close}'." 305: end
# File lib/nukumi2/vendor/tangerine.rb, line 200 200: def selection 201: self[@mark...@cursor-1] 202: end
# File lib/nukumi2/vendor/tangerine.rb, line 204 204: def selection_to_end 205: self[@mark..-1] 206: end