class DocxTemplater::TemplateProcessor

Attributes

data[R]
escape_html[R]

Public Class Methods

new(data, escape_html = true) click to toggle source

data is expected to be a hash of symbols => string or arrays of hashes.

# File lib/docx_templater/template_processor.rb, line 8
def initialize(data, escape_html = true)
  @data = data
  @escape_html = escape_html
end

Public Instance Methods

render(document) click to toggle source
# File lib/docx_templater/template_processor.rb, line 13
def render(document)
  document.force_encoding(Encoding::UTF_8) if document.respond_to?(:force_encoding)
  data.each do |key, value|
    if value.class == Array
      document = enter_multiple_values(document, key)
      document.gsub!("#SUM:#{key.to_s.upcase}#", value.count.to_s)
    else
      document.gsub!("$#{key.to_s.upcase}$", safe(value))
    end
  end
  document
end

Private Instance Methods

enter_multiple_values(document, key) click to toggle source
# File lib/docx_templater/template_processor.rb, line 36
def enter_multiple_values(document, key)
  DocxTemplater.log("enter_multiple_values for: #{key}")
  # TODO: ideally we would not re-parse xml doc every time
  xml = Nokogiri::XML(document)

  begin_row = "#BEGIN_ROW:#{key.to_s.upcase}#"
  end_row = "#END_ROW:#{key.to_s.upcase}#"
  begin_row_template = xml.xpath("//w:tr[contains(., '#{begin_row}')]", xml.root.namespaces).first
  end_row_template = xml.xpath("//w:tr[contains(., '#{end_row}')]", xml.root.namespaces).first
  DocxTemplater.log("begin_row_template: #{begin_row_template}")
  DocxTemplater.log("end_row_template: #{end_row_template}")
  fail "unmatched template markers: #{begin_row} nil: #{begin_row_template.nil?}, #{end_row} nil: #{end_row_template.nil?}. This could be because word broke up tags with it's own xml entries. See README." unless begin_row_template && end_row_template

  row_templates = []
  row = begin_row_template.next_sibling
  while row != end_row_template
    row_templates.unshift(row)
    row = row.next_sibling
  end
  DocxTemplater.log("row_templates: (#{row_templates.count}) #{row_templates.map(&:to_s).inspect}")

  # for each data, reversed so they come out in the right order
  data[key].reverse_each do |each_data|
    DocxTemplater.log("each_data: #{each_data.inspect}")

    # dup so we have new nodes to append
    row_templates.map(&:dup).each do |new_row|
      DocxTemplater.log("   new_row: #{new_row}")
      innards = new_row.inner_html
      matches = innards.scan(/\$EACH:([^\$]+)\$/)
      unless matches.empty?
        DocxTemplater.log("   matches: #{matches.inspect}")
        matches.map(&:first).each do |each_key|
          DocxTemplater.log("      each_key: #{each_key}")
          innards.gsub!("$EACH:#{each_key}$", safe(each_data[each_key.downcase.to_sym]))
        end
      end
      # change all the internals of the new node, even if we did not template
      new_row.inner_html = innards
      # DocxTemplater::log("new_row new innards: #{new_row.inner_html}")

      begin_row_template.add_next_sibling(new_row)
    end
  end
  (row_templates + [begin_row_template, end_row_template]).each(&:unlink)
  xml.to_s
end
safe(text) click to toggle source
# File lib/docx_templater/template_processor.rb, line 28
def safe(text)
  if escape_html
    text.to_s.gsub('&', '&amp;').gsub('>', '&gt;').gsub('<', '&lt;')
  else
    text.to_s
  end
end