From 50b51e3e1d630d3a6307d8da35f0fefefbf8aaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D0=B5=D0=B4=D0=BE=D1=81=D0=BE=D0=B2=20=D0=A1=D0=B5?= =?UTF-8?q?=D1=80=D0=B3=D0=B5=D0=B9?= Date: Mon, 29 May 2017 13:34:30 +0300 Subject: [PATCH 1/5] skip unmatched array --- .rubocop_todo.yml | 26 ++++++++++++------------ lib/docx_templater/docx_creator.rb | 8 +++++--- lib/docx_templater/template_processor.rb | 10 ++++++--- spec/template_processor_spec.rb | 16 +++++++++++++++ 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2979906..a5baa9e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,11 +1,19 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2017-05-03 18:43:13 +0300 using RuboCop version 0.48.1. +# on 2017-05-29 13:33:52 +0300 using RuboCop version 0.49.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent +Layout/IndentHeredoc: + Exclude: + - 'spec/template_processor_spec.rb' + # Offense count: 2 Metrics/AbcSize: Max: 54 @@ -13,18 +21,18 @@ Metrics/AbcSize: # Offense count: 5 # Configuration parameters: CountComments, ExcludedMethods. Metrics/BlockLength: - Max: 177 + Max: 191 -# Offense count: 23 +# Offense count: 26 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Metrics/LineLength: - Max: 255 + Max: 211 # Offense count: 2 # Configuration parameters: CountComments. Metrics/MethodLength: - Max: 35 + Max: 38 # Offense count: 3 Style/Documentation: @@ -34,11 +42,3 @@ Style/Documentation: - 'lib/docx_templater.rb' - 'lib/docx_templater/docx_creator.rb' - 'lib/docx_templater/template_processor.rb' - -# Offense count: 4 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent -Style/IndentHeredoc: - Exclude: - - 'spec/template_processor_spec.rb' diff --git a/lib/docx_templater/docx_creator.rb b/lib/docx_templater/docx_creator.rb index c4e4c9a..606cbd0 100644 --- a/lib/docx_templater/docx_creator.rb +++ b/lib/docx_templater/docx_creator.rb @@ -4,9 +4,9 @@ module DocxTemplater class DocxCreator attr_reader :template_path, :template_processor - def initialize(template_path, data, escape_html = true) + def initialize(template_path, data, escape_html = true, skip_unmatched: false) @template_path = template_path - @template_processor = TemplateProcessor.new(data, escape_html) + @template_processor = TemplateProcessor.new(data, escape_html, skip_unmatched: skip_unmatched) end def generate_docx_file(file_name = "output_#{Time.now.strftime('%Y-%m-%d_%H%M')}.docx") @@ -20,7 +20,9 @@ def generate_docx_bytes Zip::File.open(template_path).each do |entry| entry_name = entry.name out.put_next_entry(entry_name) - out.write(copy_or_template(entry_name, entry.get_input_stream.read)) + unless entry.directory? + out.write(copy_or_template(entry_name, entry.get_input_stream.read)) + end end end end diff --git a/lib/docx_templater/template_processor.rb b/lib/docx_templater/template_processor.rb index ae12348..633d2fe 100644 --- a/lib/docx_templater/template_processor.rb +++ b/lib/docx_templater/template_processor.rb @@ -2,12 +2,13 @@ module DocxTemplater class TemplateProcessor - attr_reader :data, :escape_html + attr_reader :data, :escape_html, :skip_unmatched # data is expected to be a hash of symbols => string or arrays of hashes. - def initialize(data, escape_html = true) + def initialize(data, escape_html = true, skip_unmatched: false) @data = data @escape_html = escape_html + @skip_unmatched = skip_unmatched end def render(document) @@ -51,7 +52,10 @@ def enter_multiple_values(document, key) 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}") - raise "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 + unless begin_row_template && end_row_template + return document if @skip_unmatched + raise "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." + end row_templates = [] row = begin_row_template.next_sibling diff --git a/spec/template_processor_spec.rb b/spec/template_processor_spec.rb index ed20170..b708d3b 100644 --- a/spec/template_processor_spec.rb +++ b/spec/template_processor_spec.rb @@ -75,6 +75,22 @@ module TestData expect(out).to include('23rd

&

#1 floor') end end + context 'when unmatched array' do + let(:unmatched_data) do + data.merge(unmatched_array: ['Some data']) + end + + it 'raised' do + parser = DocxTemplater::TemplateProcessor.new(unmatched_data) + expect { parser.render(xml) }.to raise_error + end + + it 'skipped' do + parser = DocxTemplater::TemplateProcessor.new(unmatched_data, skip_unmatched: true) + out = parser.render(xml) + expect(Nokogiri::XML.parse(out)).to be_xml + end + end end context 'unmatched begin and end row templates' do From c381101fcd08ac140896769bb02fcbc5b39220c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D0=B5=D0=B4=D0=BE=D1=81=D0=BE=D0=B2=20=D0=A1=D0=B5?= =?UTF-8?q?=D1=80=D0=B3=D0=B5=D0=B9?= Date: Tue, 30 May 2017 10:56:17 +0300 Subject: [PATCH 2/5] allow recursive array --- lib/docx_templater/template_processor.rb | 46 ++++++++++++++---------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/docx_templater/template_processor.rb b/lib/docx_templater/template_processor.rb index 633d2fe..46fc61b 100644 --- a/lib/docx_templater/template_processor.rb +++ b/lib/docx_templater/template_processor.rb @@ -16,7 +16,7 @@ def render(document) data.each do |key, value| case value when Array - document = enter_multiple_values(document, key) + document = recursive_call_array(document, key, data[key]) document.gsub!("#SUM:#{key.to_s.upcase}#", value.count.to_s) when TrueClass, FalseClass if value @@ -41,17 +41,13 @@ def safe(text) end end - 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) + def enter_multiple_values xml, key, values + xml = Nokogiri::XML(xml) 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}") unless begin_row_template && end_row_template return document if @skip_unmatched raise "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." @@ -63,33 +59,47 @@ def enter_multiple_values(document, key) 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}") + values.reverse_each do |data| + rt = row_templates.map(&:dup) + + each_data = {} + data.each do |k, v| + if v.is_a?(Array) + doc = Nokogiri::XML::Document.new + root = doc.create_element 'pseudo_root', xml.root.namespaces + root.inner_html = rt.reverse.map{|x| x.to_xml}.join + q = recursive_call_array root.to_xml, k, v + rt = xml.parse(q).reverse + else + each_data[k] = v + end + end + # dup so we have new nodes to append - row_templates.map(&:dup).each do |new_row| - DocxTemplater.log(" new_row: #{new_row}") + rt.map(&:dup).each do |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])) + innards.gsub!("$EACH:#{each_key}$", each_data[each_key.downcase.to_s]) 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 + if xml.root.name == 'pseudo_root' + xml.root.inner_html + else + xml.to_s + end end + end end From e8b0cc70c4e35c93c53e31ad7a94bb31d4ff3b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D0=B5=D0=B4=D0=BE=D1=81=D0=BE=D0=B2=20=D0=A1=D0=B5?= =?UTF-8?q?=D1=80=D0=B3=D0=B5=D0=B9?= Date: Tue, 30 May 2017 11:13:24 +0300 Subject: [PATCH 3/5] recover log --- lib/docx_templater/template_processor.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/docx_templater/template_processor.rb b/lib/docx_templater/template_processor.rb index 46fc61b..4f0dce3 100644 --- a/lib/docx_templater/template_processor.rb +++ b/lib/docx_templater/template_processor.rb @@ -48,6 +48,8 @@ def enter_multiple_values xml, key, values 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}") unless begin_row_template && end_row_template return document if @skip_unmatched raise "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." @@ -59,9 +61,11 @@ def enter_multiple_values xml, key, values 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 values.reverse_each do |data| + DocxTemplater.log("each_data: #{values.inspect}") rt = row_templates.map(&:dup) each_data = {} @@ -80,16 +84,19 @@ def enter_multiple_values xml, key, values # dup so we have new nodes to append rt.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}$", each_data[each_key.downcase.to_s]) 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 From 34efa0b49e4a96bfaa917b8937fcbc2251411352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D0=B5=D0=B4=D0=BE=D1=81=D0=BE=D0=B2=20=D0=A1=D0=B5?= =?UTF-8?q?=D1=80=D0=B3=D0=B5=D0=B9?= Date: Tue, 30 May 2017 11:21:01 +0300 Subject: [PATCH 4/5] fix name method --- lib/docx_templater/template_processor.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docx_templater/template_processor.rb b/lib/docx_templater/template_processor.rb index 4f0dce3..5254a19 100644 --- a/lib/docx_templater/template_processor.rb +++ b/lib/docx_templater/template_processor.rb @@ -16,7 +16,7 @@ def render(document) data.each do |key, value| case value when Array - document = recursive_call_array(document, key, data[key]) + document = enter_multiple_values(document, key, data[key]) document.gsub!("#SUM:#{key.to_s.upcase}#", value.count.to_s) when TrueClass, FalseClass if value @@ -74,7 +74,7 @@ def enter_multiple_values xml, key, values doc = Nokogiri::XML::Document.new root = doc.create_element 'pseudo_root', xml.root.namespaces root.inner_html = rt.reverse.map{|x| x.to_xml}.join - q = recursive_call_array root.to_xml, k, v + q = enter_multiple_values root.to_xml, k, v rt = xml.parse(q).reverse else each_data[k] = v From 0bd147cabcab60550e90acd2b3c06e2c191f967c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A4=D0=B5=D0=B4=D0=BE=D1=81=D0=BE=D0=B2=20=D0=A1=D0=B5?= =?UTF-8?q?=D1=80=D0=B3=D0=B5=D0=B9?= Date: Wed, 31 May 2017 16:52:38 +0300 Subject: [PATCH 5/5] fix test --- lib/docx_templater/template_processor.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/docx_templater/template_processor.rb b/lib/docx_templater/template_processor.rb index 5254a19..c27e56d 100644 --- a/lib/docx_templater/template_processor.rb +++ b/lib/docx_templater/template_processor.rb @@ -51,7 +51,7 @@ def enter_multiple_values xml, key, values DocxTemplater.log("begin_row_template: #{begin_row_template}") DocxTemplater.log("end_row_template: #{end_row_template}") unless begin_row_template && end_row_template - return document if @skip_unmatched + return as_result(xml) if @skip_unmatched raise "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." end @@ -65,7 +65,7 @@ def enter_multiple_values xml, key, values # for each data, reversed so they come out in the right order values.reverse_each do |data| - DocxTemplater.log("each_data: #{values.inspect}") + DocxTemplater.log("each_data: #{data.inspect}") rt = row_templates.map(&:dup) each_data = {} @@ -91,7 +91,7 @@ def enter_multiple_values xml, key, values DocxTemplater.log(" matches: #{matches.inspect}") matches.map(&:first).each do |each_key| DocxTemplater.log(" each_key: #{each_key}") - innards.gsub!("$EACH:#{each_key}$", each_data[each_key.downcase.to_s]) + 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 @@ -101,6 +101,10 @@ def enter_multiple_values xml, key, values end end (row_templates + [begin_row_template, end_row_template]).each(&:unlink) + as_result xml + end + + def as_result xml if xml.root.name == 'pseudo_root' xml.root.inner_html else