docstache 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'template_processor_spec'
3
+
4
+ describe 'integration test', integration: true do
5
+ let(:data) { Docstache::TestData::DATA }
6
+ let(:base_path) { SPEC_BASE_PATH.join('example_input') }
7
+ let(:input_file) { "#{base_path}/ExampleTemplate.docx" }
8
+ let(:output_dir) { "#{base_path}/tmp" }
9
+ let(:output_file) { "#{output_dir}/IntegrationTestOutput.docx" }
10
+ before do
11
+ FileUtils.rm_rf(output_dir) if File.exist?(output_dir)
12
+ Dir.mkdir(output_dir)
13
+ end
14
+
15
+ context 'should process in incoming docx' do
16
+ it 'generates a valid zip file (.docx)' do
17
+ Docstache::Render.new(input_file, data).generate_docx_file(output_file)
18
+
19
+ archive = Zip::File.open(output_file)
20
+ archive.close
21
+
22
+ puts "\n************************************"
23
+ puts ' >>> Only will work on mac <<<'
24
+ puts 'NOW attempting to open created file in Word.'
25
+ cmd = "open #{output_file}"
26
+ puts " will run '#{cmd}'"
27
+ puts '************************************'
28
+
29
+ system cmd
30
+ end
31
+
32
+ it 'generates a file with the same contents as the input docx' do
33
+ input_entries = Zip::File.open(input_file) { |z| z.map(&:name) }
34
+ DocxTemplater::DocxCreator.new(input_file, data).generate_docx_file(output_file)
35
+ output_entries = Zip::File.open(output_file) { |z| z.map(&:name) }
36
+
37
+ expect(input_entries).to eq(output_entries)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ require 'fileutils'
2
+ require 'docstache'
3
+
4
+ SPEC_BASE_PATH = Pathname.new(File.expand_path(File.dirname(__FILE__)))
5
+
6
+ RSpec.configure do |config|
7
+ [:expect_with, :mock_with].each do |method|
8
+ config.send(method, :rspec) do |c|
9
+ c.syntax = :expect
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,222 @@
1
+ require 'spec_helper'
2
+ require 'nokogiri'
3
+
4
+ module Docstache
5
+ module TestData
6
+ DATA = {
7
+ teacher: 'Priya Vora',
8
+ building: 'Building #14',
9
+ classroom: 'Rm 202'.to_sym,
10
+ district: 'Washington County Public Schools',
11
+ senority: 12.25,
12
+ roster: [
13
+ { name: 'Sally', age: 12, attendence: '100%' },
14
+ { name: :Xiao, age: 10, attendence: '94%' },
15
+ { name: 'Bryan', age: 13, attendence: '100%' },
16
+ { name: 'Larry', age: 11, attendence: '90%' },
17
+ { name: 'Kumar', age: 12, attendence: '76%' },
18
+ { name: 'Amber', age: 11, attendence: '100%' },
19
+ { name: 'Isaiah', age: 12, attendence: '89%' },
20
+ { name: 'Omar', age: 12, attendence: '99%' },
21
+ { name: 'Xi', age: 11, attendence: '20%' },
22
+ { name: 'Noushin', age: 12, attendence: '100%' }
23
+ ],
24
+ event_reports: [
25
+ { name: 'Science Museum Field Trip', notes: 'PTA sponsored event. Spoke to Astronaut with HAM radio.' },
26
+ { name: 'Wilderness Center Retreat', notes: '2 days hiking for charity:water fundraiser, $10,200 raised.' }
27
+ ],
28
+ created_at: '11-12-03 02:01'
29
+ }
30
+ end
31
+ end
32
+
33
+ describe Docstache::Render do
34
+ let(:data) { Marshal.load(Marshal.dump(DocxTemplater::TestData::DATA)) } # deep copy
35
+ let(:base_path) { SPEC_BASE_PATH.join('example_input') }
36
+ let(:xml) { File.read("#{base_path}/word/document.xml") }
37
+ let(:parser) { DocxTemplater::TemplateProcessor.new(data) }
38
+
39
+ context 'valid xml' do
40
+ it 'should render and still be valid XML' do
41
+ expect(Nokogiri::XML.parse(xml)).to be_xml
42
+ out = parser.render(xml)
43
+ expect(Nokogiri::XML.parse(out)).to be_xml
44
+ end
45
+
46
+ it 'should accept non-ascii characters' do
47
+ data[:teacher] = '老师'
48
+ out = parser.render(xml)
49
+ expect(out).to include('老师')
50
+ expect(Nokogiri::XML.parse(out)).to be_xml
51
+ end
52
+
53
+ it 'should escape as necessary invalid xml characters, if told to' do
54
+ data[:building] = '23rd & A #1 floor'
55
+ data[:classroom] = '--> 201 <!--'
56
+ data[:roster][0][:name] = '<#Ai & Bo>'
57
+ out = parser.render(xml)
58
+
59
+ expect(Nokogiri::XML.parse(out)).to be_xml
60
+ expect(out).to include('23rd &amp; A #1 floor')
61
+ expect(out).to include('--&gt; 201 &lt;!--')
62
+ expect(out).to include('&lt;#Ai &amp; Bo&gt;')
63
+ end
64
+
65
+ context 'not escape xml' do
66
+ let(:parser) { DocxTemplater::TemplateProcessor.new(data, false) }
67
+ it 'does not escape the xml attributes' do
68
+ data[:building] = '23rd <p>&amp;</p> #1 floor'
69
+ out = parser.render(xml)
70
+ expect(Nokogiri::XML.parse(out)).to be_xml
71
+ expect(out).to include('23rd <p>&amp;</p> #1 floor')
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'unmatched begin and end row templates' do
77
+ it 'should not raise' do
78
+ xml = <<EOF
79
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
80
+ <w:body>
81
+ <w:tbl>
82
+ <w:tr><w:tc>
83
+ <w:p>
84
+ <w:r><w:t>#BEGIN_ROW:#{:roster.to_s.upcase}#</w:t></w:r>
85
+ </w:p>
86
+ </w:tc></w:tr>
87
+ <w:tr><w:tc>
88
+ <w:p>
89
+ <w:r><w:t>#END_ROW:#{:roster.to_s.upcase}#</w:t></w:r>
90
+ </w:p>
91
+ </w:tc></w:tr>
92
+ <w:tr><w:tc>
93
+ <w:p>
94
+ <w:r><w:t>#BEGIN_ROW:#{:event_reports.to_s.upcase}#</w:t></w:r>
95
+ </w:p>
96
+ </w:tc></w:tr>
97
+ <w:tr><w:tc>
98
+ <w:p>
99
+ <w:r><w:t>#END_ROW:#{:event_reports.to_s.upcase}#</w:t></w:r>
100
+ </w:p>
101
+ </w:tc></w:tr>
102
+ </w:tbl>
103
+ </w:body>
104
+ </xml>
105
+ EOF
106
+ expect { parser.render(xml) }.to_not raise_error
107
+ end
108
+
109
+ it 'should raise an exception' do
110
+ xml = <<EOF
111
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
112
+ <w:body>
113
+ <w:tbl>
114
+ <w:tr><w:tc>
115
+ <w:p>
116
+ <w:r><w:t>#BEGIN_ROW:#{:roster.to_s.upcase}#</w:t></w:r>
117
+ </w:p>
118
+ </w:tc></w:tr>
119
+ <w:tr><w:tc>
120
+ <w:p>
121
+ <w:r><w:t>#END_ROW:#{:roster.to_s.upcase}#</w:t></w:r>
122
+ </w:p>
123
+ </w:tc></w:tr>
124
+ <w:tr><w:tc>
125
+ <w:p>
126
+ <w:r><w:t>#BEGIN_ROW:#{:event_reports.to_s.upcase}#</w:t></w:r>
127
+ </w:p>
128
+ </w:tc></w:tr>
129
+ </w:tbl>
130
+ </w:body>
131
+ </xml>
132
+ EOF
133
+ expect { parser.render(xml) }.to raise_error(/#END_ROW:EVENT_REPORTS# nil: true/)
134
+ end
135
+ end
136
+
137
+ it 'should enter no text for a nil value' do
138
+ xml = <<EOF
139
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
140
+ <w:body>
141
+ <w:p>Before.$KEY$After</w:p>
142
+ </w:body>
143
+ </xml>
144
+ EOF
145
+ actual = DocxTemplater::TemplateProcessor.new(key: nil).render(xml)
146
+ expected_xml = <<EOF
147
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
148
+ <w:body>
149
+ <w:p>Before.After</w:p>
150
+ </w:body>
151
+ </xml>
152
+ EOF
153
+ expect(actual).to eq(expected_xml)
154
+ end
155
+
156
+ it 'should replace all simple keys with values' do
157
+ non_array_keys = data.reject { |_, v| v.is_a?(Array) }
158
+ non_array_keys.keys.each do |key|
159
+ expect(xml).to include("$#{key.to_s.upcase}$")
160
+ expect(xml).not_to include(data[key].to_s)
161
+ end
162
+ out = parser.render(xml)
163
+
164
+ non_array_keys.each do |key|
165
+ expect(out).not_to include("$#{key}$")
166
+ expect(out).to include(data[key].to_s)
167
+ end
168
+ end
169
+
170
+ it 'should replace all array keys with values' do
171
+ expect(xml).to include('#BEGIN_ROW:')
172
+ expect(xml).to include('#END_ROW:')
173
+ expect(xml).to include('$EACH:')
174
+
175
+ out = parser.render(xml)
176
+
177
+ expect(out).not_to include('#BEGIN_ROW:')
178
+ expect(out).not_to include('#END_ROW:')
179
+ expect(out).not_to include('$EACH:')
180
+
181
+ [:roster, :event_reports].each do |key|
182
+ data[key].each do |row|
183
+ row.values.map(&:to_s).each do |row_value|
184
+ expect(out).to include(row_value)
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ it 'shold render students names in the same order as the data' do
191
+ out = parser.render(xml)
192
+ expect(out).to include('Sally')
193
+ expect(out).to include('Kumar')
194
+ expect(out.index('Kumar')).to be > out.index('Sally')
195
+ end
196
+
197
+ it 'shold render event reports names in the same order as the data' do
198
+ out = parser.render(xml)
199
+ expect(out).to include('Science Museum Field Trip')
200
+ expect(out).to include('Wilderness Center Retreat')
201
+ expect(out.index('Wilderness Center Retreat')).to be > out.index('Science Museum Field Trip')
202
+ end
203
+
204
+ it 'should render 2-line event reports in same order as docx' do
205
+ event_reports_starting_at = xml.index('#BEGIN_ROW:EVENT_REPORTS#')
206
+ expect(event_reports_starting_at).to be >= 0
207
+ expect(xml.index('$EACH:NAME$', event_reports_starting_at)).to be > event_reports_starting_at
208
+ expect(xml.index('$EACH:NOTES$', event_reports_starting_at)).to be > event_reports_starting_at
209
+ expect(xml.index('$EACH:NOTES$', event_reports_starting_at)).to be > xml.index('$EACH:NAME$', event_reports_starting_at)
210
+
211
+ out = parser.render(xml)
212
+ expect(out.index('PTA sponsored event. Spoke to Astronaut with HAM radio.')).to be > out.index('Science Museum Field Trip')
213
+ end
214
+
215
+ it 'should render sums of input data' do
216
+ expect(xml).to include('#SUM')
217
+ out = parser.render(xml)
218
+ expect(out).not_to include('#SUM')
219
+ expect(out).to include("#{data[:roster].count} Students")
220
+ expect(out).to include("#{data[:event_reports].count} Events")
221
+ end
222
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docstache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Will Cosgrove
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubyzip
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ description: Integrates data into MS Word docx template files. Processing supports
42
+ loops and replacement of strings of data both outside and within loops.
43
+ email:
44
+ - will@willcosgrove.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.rdoc
53
+ - Rakefile
54
+ - docstache.gemspec
55
+ - lib/docstache.rb
56
+ - lib/docstache/document.rb
57
+ - lib/docstache/renderer.rb
58
+ - lib/docstache/version.rb
59
+ - spec/example_input/ExampleTemplate.docx
60
+ - spec/example_input/word/document.xml
61
+ - spec/integration_spec.rb
62
+ - spec/spec_helper.rb
63
+ - spec/template_processor_spec.rb
64
+ homepage: https://github.com/willcosgrove/docstache
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Merges Hash of Data into Word docx template files using mustache syntax
88
+ test_files:
89
+ - spec/example_input/ExampleTemplate.docx
90
+ - spec/example_input/word/document.xml
91
+ - spec/integration_spec.rb
92
+ - spec/spec_helper.rb
93
+ - spec/template_processor_spec.rb
94
+ has_rdoc: