Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/docx/document_replacer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
20 changes: 14 additions & 6 deletions lib/docx/placeholder_observer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
104 changes: 72 additions & 32 deletions spec/lib/docx/placeholder_observer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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