lm_docstache 1.1.0

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,78 @@
1
+ require 'spec_helper'
2
+ require 'active_support/core_ext/object/blank.rb'
3
+
4
+ module LMDocstache
5
+ module TestData
6
+ DATA = {
7
+ teacher: 'Johhny Bissel',
8
+ building: 'Building #14',
9
+ classroom: 'Rm 202'.to_sym,
10
+ district: 'San Deigo Unified School District',
11
+ seniority: 12.25,
12
+ roster: [
13
+ { name: 'Sally', age: 12, attendance: '100%' },
14
+ { name: :Xiao, age: 10, attendance: '94%' },
15
+ { name: 'Bryan', age: 13, attendance: '100%' },
16
+ { name: 'Larry', age: 11, attendance: '90%' },
17
+ { name: 'Kumar', age: 12, attendance: '76%' },
18
+ { name: 'Amber', age: 11, attendance: '100%' },
19
+ { name: 'Isaiah', age: 12, attendance: '89%' },
20
+ { name: 'Omar', age: 12, attendance: '99%' },
21
+ { name: 'Xi', age: 11, attendance: '20%' },
22
+ { name: 'Noushin', age: 12, attendance: '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-20 02:01',
29
+ true_cond: true,
30
+ false_cond: false
31
+ }
32
+ end
33
+ end
34
+
35
+ describe 'integration test', integration: true do
36
+ let(:data) { LMDocstache::TestData::DATA }
37
+ let(:base_path) { SPEC_BASE_PATH.join('example_input') }
38
+ let(:input_file) { "#{base_path}/ExampleTemplate.docx" }
39
+ let(:output_dir) { "#{base_path}/tmp" }
40
+ let(:output_file) { "#{output_dir}/IntegrationTestOutput.docx" }
41
+ let(:document) { LMDocstache::Document.new(input_file) }
42
+ before do
43
+ FileUtils.rm_rf(output_dir) if File.exist?(output_dir)
44
+ Dir.mkdir(output_dir)
45
+ end
46
+
47
+ context 'should process that incoming docx' do
48
+ it 'loads the input file' do
49
+ expect(document).to_not be_nil
50
+ end
51
+
52
+ it 'generates output file with the same contents as the input file' do
53
+ input_entries = Zip::File.open(input_file) { |z| z.map(&:name) }
54
+ document.save(output_file)
55
+ output_entries = Zip::File.open(output_file) { |z| z.map(&:name) }
56
+
57
+ expect(input_entries - output_entries).to be_empty
58
+ end
59
+
60
+ it 'fixes nested xml errors breaking tags' do
61
+ expect(document.send(:problem_paragraphs)).to_not be_empty
62
+ document.fix_errors
63
+ expect(document.send(:problem_paragraphs)).to be_empty
64
+ end
65
+
66
+ it 'has the expected amount of usable tags' do
67
+ expect(document.usable_tags.count).to be(27)
68
+ end
69
+
70
+ it 'has the expected amount of unique tag names' do
71
+ expect(document.usable_tag_names.count).to be(16)
72
+ end
73
+
74
+ it 'renders file using data' do
75
+ document.render_file(output_file, data)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,12 @@
1
+ require 'fileutils'
2
+ require_relative '../lib/lm_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 LMDocstache
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 LMDocstache::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,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lm_docstache
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Roey Chasman
8
+ - Will Cosgrove
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-06-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.6'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rubyzip
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.1'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 3.1.0
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 3.1.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry-byebug
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '1'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '1'
70
+ description: Integrates data into MS Word docx template files. Processing supports
71
+ loops and replacement of strings of data both outside and within loops.
72
+ email:
73
+ - roey@lawmatics.com
74
+ - will@willcosgrove.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - CHANGELOG.md
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - lib/lm_docstache.rb
86
+ - lib/lm_docstache/block.rb
87
+ - lib/lm_docstache/data_scope.rb
88
+ - lib/lm_docstache/document.rb
89
+ - lib/lm_docstache/renderer.rb
90
+ - lib/lm_docstache/version.rb
91
+ - lm_docstache.gemspec
92
+ - spec/data_scope_spec.rb
93
+ - spec/empty_data_scope_spec.rb
94
+ - spec/example_input/ExampleTemplate.docx
95
+ - spec/example_input/word/document.xml
96
+ - spec/integration_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/template_processor_spec.rb
99
+ homepage: https://www.lawmatics.com
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.0.3
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Merges Hash of Data into Word docx template files using mustache syntax
122
+ test_files:
123
+ - spec/data_scope_spec.rb
124
+ - spec/empty_data_scope_spec.rb
125
+ - spec/example_input/ExampleTemplate.docx
126
+ - spec/example_input/word/document.xml
127
+ - spec/integration_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/template_processor_spec.rb