From 00673bb2e16541d23dc96cd49ddee1e85d1086c0 Mon Sep 17 00:00:00 2001 From: corsair Date: Thu, 22 Apr 2021 10:54:22 +0300 Subject: [PATCH] Add support for different tags --- lib/docx/document_replacer.rb | 2 +- lib/docx/placeholder_observer.rb | 20 ++-- spec/lib/docx/placeholder_observer_spec.rb | 104 ++++++++++++++------- 3 files changed, 87 insertions(+), 39 deletions(-) diff --git a/lib/docx/document_replacer.rb b/lib/docx/document_replacer.rb index 5ffca23..397fff5 100644 --- a/lib/docx/document_replacer.rb +++ b/lib/docx/document_replacer.rb @@ -8,7 +8,7 @@ class DocumentReplacer def initialize(str, data_provider, opts = {}) @doc = REXML::Document.new(str) - @observer = Docx::PlaceholderObserver.new(data_provider) + @observer = Docx::PlaceholderObserver.new(data_provider, opts) walk_node(doc.root) @observer.end_of_document convert_newlines if opts.fetch(:convert_newlines){ true } diff --git a/lib/docx/placeholder_observer.rb b/lib/docx/placeholder_observer.rb index 372c4ea..c9b0abd 100644 --- a/lib/docx/placeholder_observer.rb +++ b/lib/docx/placeholder_observer.rb @@ -3,7 +3,10 @@ module Docx class PlaceholderObserver attr_reader :data_provider - def initialize(data_provider) + def initialize(data_provider, opts = {}) + @delimiter = opts.fetch(:delimiter, '||').to_s + raise ArgumentError.new('Delimiter must be present.') if delimiter.nil? || delimiter.strip.empty? + raise ArgumentError.new('Delimiter must consist of same characters.') if delimiter_chars_not_same @data_provider = data_provider @buffer = '' @state = :waiting_for_opening @@ -23,16 +26,16 @@ def end_of_document private - attr_accessor :state, :buffer, :nodes_to_fix + attr_accessor :state, :buffer, :nodes_to_fix, :delimiter def next_char(node, index, c) send(state, node, index, c) end def waiting_for_opening(node, index, c) - if c == '|' + if c == delimiter[0] add_char_to_buffer(node,index,c) - if buffer == '||' + if buffer == delimiter self.state = :capturing_placeholder end else @@ -42,8 +45,8 @@ def waiting_for_opening(node, index, c) def capturing_placeholder(node, index, c) add_char_to_buffer(node,index,c) - if buffer[-2..-1] == '||' - key = buffer[2..-3] + if buffer[-delimiter.length..-1] == delimiter + key = buffer[delimiter.length..-(delimiter.length + 1)] if data_provider.has_key?(key.to_sym) new_value = data_provider[key.to_sym] save_fix_for_later(new_value) @@ -75,5 +78,10 @@ def make_fixes end @fixes_to_make = [] end + + def delimiter_chars_not_same + first_char = delimiter[0] + delimiter.split(//).any? { |c| c != first_char } + end end end diff --git a/spec/lib/docx/placeholder_observer_spec.rb b/spec/lib/docx/placeholder_observer_spec.rb index 65e2933..2ef4dcf 100644 --- a/spec/lib/docx/placeholder_observer_spec.rb +++ b/spec/lib/docx/placeholder_observer_spec.rb @@ -8,46 +8,86 @@ r.stub(:has_key? => true) r end - it "finds placeholders as it is given text nodes" do - data_provider.should_receive(:[]).with(:title).and_return("The Thing") - n1 = REXML::Text.new("dflkja sdf ||title|| slkjasdlkj") - n1.should_receive(:value=).with('dflkja sdf The Thing slkjasdlkj') - subject.next_node(n1) - subject.end_of_document + shared_examples 'test delimiter' do + it "finds placeholders as it is given text nodes" do + data_provider.should_receive(:[]).with(:title).and_return("The Thing") + n1 = REXML::Text.new("dflkja sdf #{delimiter}title#{delimiter} slkjasdlkj") + n1.should_receive(:value=).with('dflkja sdf The Thing slkjasdlkj') + + subject.next_node(n1) + subject.end_of_document + end + + + it "handles multiple placeholders in a single node correctly" do + data_provider.should_receive(:[]).with(:title).and_return('Zombie Apocalypse') + data_provider.should_receive(:[]).with(:subject).and_return('Movie') + n1 = REXML::Text.new("#{delimiter}title#{delimiter} is a #{delimiter}subject#{delimiter}. Okay?") + + subject.next_node(n1) + subject.end_of_document + n1.value.should == 'Zombie Apocalypse is a Movie. Okay?' + end + + it "handles nil replacements" do + data_provider.should_receive(:[]).with(:title).and_return(nil) + n1 = REXML::Text.new("#{delimiter}title#{delimiter}") + subject.next_node(n1) + subject.end_of_document + n1.value.should == '' + end end - it "finds placeholders among several nodes" do - data_provider.should_receive(:[]).with(:title).and_return('The Thing') - n1 = REXML::Text.new('booyah, (&&IJH))OJ |') - n1.should_receive(:value=).with('booyah, (&&IJH))OJ The Thing') - n2 = REXML::Text.new('|tit') - n2.should_receive(:value=).with('') - n3 = REXML::Text.new('le|| asdf093n38hfaj') - n3.should_receive(:value=).with(' asdf093n38hfaj') - - subject.next_node(n1) - subject.next_node(n2) - subject.next_node(n3) - subject.end_of_document + context 'default delimiter' do + let(:delimiter) { '||' } + subject{ Docx::PlaceholderObserver.new(data_provider) } + + include_examples 'test delimiter' + + it "finds placeholders among several nodes" do + data_provider.should_receive(:[]).with(:title).and_return('The Thing') + n1 = REXML::Text.new('booyah, (&&IJH))OJ |') + n1.should_receive(:value=).with('booyah, (&&IJH))OJ The Thing') + n2 = REXML::Text.new('|tit') + n2.should_receive(:value=).with('') + n3 = REXML::Text.new('le|| asdf093n38hfaj') + n3.should_receive(:value=).with(' asdf093n38hfaj') + + subject.next_node(n1) + subject.next_node(n2) + subject.next_node(n3) + subject.end_of_document + end + end + + context 'custom delimiter' do + let(:delimiter) { '#' } + subject{ Docx::PlaceholderObserver.new(data_provider, delimiter: delimiter) } + + include_examples 'test delimiter' end - it "handles multiple placeholders in a single node correctly" do - data_provider.should_receive(:[]).with(:title).and_return('Zombie Apocalypse') - data_provider.should_receive(:[]).with(:subject).and_return('Movie') - n1 = REXML::Text.new('||title|| is a ||subject||. Okay?') + context 'when delimiter is blank' do + let(:delimiter) { ' ' } + it 'raises an error' do + expect{ Docx::PlaceholderObserver.new(data_provider, delimiter: delimiter) }.to raise_error ArgumentError + end + end - subject.next_node(n1) - subject.end_of_document - n1.value.should == 'Zombie Apocalypse is a Movie. Okay?' + context 'when delimiter is nil' do + let(:delimiter) { nil } + it 'raises an error' do + expect{ Docx::PlaceholderObserver.new(data_provider, delimiter: delimiter) }.to raise_error ArgumentError + end end - it "handles nil replacements" do - data_provider.should_receive(:[]).with(:title).and_return(nil) - n1 = REXML::Text.new('||title||') - subject.next_node(n1) - subject.end_of_document - n1.value.should == '' + context 'when delimiter contains different chars' do + let(:delimiter) { '|#' } + + it 'raises an error' do + expect{ Docx::PlaceholderObserver.new(data_provider, delimiter: delimiter) }.to raise_error ArgumentError + end end end end