class HtmlTemplate
note: HtmlTemplate
is a class (NOT a module) for now - change - why? why not?
Constants
- ALL_RE
- CATCH_CLOSE_RE
- CATCH_OPEN_RE
- ELSE_RE
- ESCAPES
- IDENT
- IF_CLOSE_RE
- IF_OPEN_RE
- LOOP_CLOSE_RE
- LOOP_OPEN_RE
- MAJOR
- MINOR
- PATCH
- VAR_RE
- VERSION
Attributes
errors[R]
names[R]
template[R]
text[R]
Public Class Methods
config()
click to toggle source
# File lib/html/template.rb, line 122 def self.config @config ||= Configuration.new end
configure() { |config| ... }
click to toggle source
lets you use
HtmlTemplate.configure do |config| config.debug = true config.strict = true end
# File lib/html/template.rb, line 118 def self.configure yield( config ) end
new( text=nil, filename: nil, strict: config.strict?, loop_vars: config.loop_vars? )
click to toggle source
# File lib/html/template.rb, line 139 def initialize( text=nil, filename: nil, strict: config.strict?, loop_vars: config.loop_vars? ) if text.nil? ## try to read file (by filename) text = File.open( filename, 'r:utf-8' ) { |f| f.read } end ## options @strict = strict @loop_vars = loop_vars ## todo/fix: add filename to ERB too (for better error reporting) @text, @errors, @names = convert( text ) ## note: keep a copy of the converted template text if @errors.size > 0 puts "!! ERROR - #{@errors.size} conversion / syntax error(s):" pp @errors raise if strict? ## todo - find a good Error - StandardError - why? why not? end @template = ERB.new( @text, nil, '%<>' ) end
root()
click to toggle source
# File lib/html/template/version.rb, line 19 def self.root File.expand_path( File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) ) end
version()
click to toggle source
# File lib/html/template/version.rb, line 10 def self.version VERSION end
Public Instance Methods
config()
click to toggle source
config convenience (shortcut) helpers
# File lib/html/template.rb, line 133 def config() self.class.config; end
convert( text )
click to toggle source
# File lib/html/template.rb, line 274 def convert( text ) errors = [] # note: reset global errros list names = Names.new ## keep track of all referenced / used names in VAR/IF/UNLESS/LOOP/etc. stack = [] ## note: convert line-by-line ## allows comments and line no reporting etc. buf = String.new('') ## note: '' required for getting source encoding AND not ASCII-8BIT!!! lineno = 0 text.each_line do |line| lineno += 1 if line.lstrip.start_with?( '#' ) ## or make it tripple ### - why? why not? buf << "%#{line.lstrip}" elsif line.strip.empty? buf << line else buf << line.gsub( ALL_RE ) do |_| m = $~ ## (global) last match object tag = m[:tag] tag_open = m[:open] tag_close = m[:close] ident = m[:ident] unknown = m[:unknown] # catch all for unknown / unmatched tags escape = m[:escape] format = m[:format] ## todo/fix: rename ctx to scope or __ - why? why not? ## note: peek; get top stack item ## if top level (stack empty) => nothing ## otherwise => channel. or item. etc. (with trailing dot included!) ctx = if stack.empty? '' else ## check for special loop variables if loop_vars? && ['__INDEX__', '__COUNTER__', '__INDEX__', '__COUNTER__', '__ODD__', '__EVEN__', '__FIRST__', '__INNER__', '__OUTER__', '__LAST__' ].include?( ident ) "#{ident_to_loop_it( stack[-1] )}_loop." else ## assume plural ident e.g. channels ## cut-off last char, that is, ## the plural s channels => channel ## note: ALWAYS downcase (auto-generated) loop iterator/pass name "#{ident_to_loop_it( stack[-1] )}." end end code = if tag == 'VAR' names.add( stack, ident, '$VAR' ) if escape && format == 'HTML' ## check or use long form e.g. CGI.escapeHTML - why? why not? "<%=h #{ctx}#{ident} %>" else "<%= #{ctx}#{ident} %>" end elsif tag == 'LOOP' && tag_open names.add( stack, ident, '$LOOP' ) ## assume plural ident e.g. channels ## cut-off last char, that is, the plural s channels => channel ## note: ALWAYS downcase (auto-generated) loop iterator/pass name it = ident_to_loop_it( ident ) stack.push( ident ) if loop_vars? "<% #{ctx}#{ident}.each_with_loop do |#{it}, #{it}_loop| %>" else "<% #{ctx}#{ident}.each do |#{it}| %>" end elsif tag == 'LOOP' && tag_close stack.pop "<% end %>" elsif tag == 'IF' && tag_open names.add( stack, ident, '$IF' ) "<% if #{ctx}#{ident} %>" elsif tag == 'UNLESS' && tag_open names.add( stack, ident, '$UNLESS' ) "<% unless #{ctx}#{ident} %>" elsif (tag == 'IF' || tag == 'UNLESS') && tag_close "<% end %>" elsif tag == 'ELSE' "<% else %>" elsif unknown errors << if tag_open "line #{lineno} - unknown open tag: #{unknown}" else ## assume tag_close "line #{lineno} - unknown close tag: #{unknown}" end puts "!! ERROR in line #{lineno} - #{errors[-1]}:" puts line "<%# !!error - #{errors[-1]} %>" else raise ArgumentError ## unknown tag #{tag} end puts " line #{lineno} - match #{m[0]} replacing with: #{code}" if debug? code end end end # each_line [buf, errors, names.to_h] end
debug?()
click to toggle source
# File lib/html/template.rb, line 134 def debug?() config.debug?; end
ident_to_loop_it( ident )
click to toggle source
# File lib/html/template.rb, line 265 def ident_to_loop_it( ident ) # make loop iterator (e.g. Channels => channel and so on) ## assume plural ident e.g. channels ## cut-off last char, that is, ## the plural s channels => channel ## note: ALWAYS downcase (auto-generated) loop iterator/pass name ident[0..-2].downcase end
loop_vars?()
click to toggle source
# File lib/html/template.rb, line 137 def loop_vars?() @loop_vars; end
render( **kwargs )
click to toggle source
# File lib/html/template.rb, line 403 def render( **kwargs ) ## todo: use locals / assigns or something instead of **kwargs - why? why not? ## allow/support (extra) locals / assigns - why? why not? ## note: Ruby >= 2.5 has ERB#result_with_hash - use later - why? why not? kwargs = kwargs.reduce( {} ) do |hash, (key, val)| ## puts "#{key} => #{val}:#{val.class.name}" hash[key] = to_recursive_ostruct( val ) hash end ## (auto-)convert array and hash values to ostruct ## for easy dot (.) access ## e.g. student.name instead of student[:name] @template.result( Context.new( **kwargs ).get_binding ) end
strict?()
click to toggle source
# File lib/html/template.rb, line 136 def strict?() @strict; end
to_recursive_ostruct( o )
click to toggle source
# File lib/html/template.rb, line 217 def to_recursive_ostruct( o ) if o.is_a?( Array ) o.reduce( [] ) do |ary, item| ary << to_recursive_ostruct( item ) ary end elsif o.is_a?( Hash ) ## puts 'to_recursive_ostruct (hash):' OpenStruct.new( o.reduce( {} ) do |hash, (key, val)| ## puts "#{key} => #{val}:#{val.class.name}" hash[key] = to_recursive_ostruct( val ) hash end ) else ## assume regular "primitive" value - pass along as is o end end