docstache 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6755758d881843dbac0475960fe9234c627bcae
4
- data.tar.gz: 5d86fa6e76ac7ca3bdde8e5a657d7b84a47cec79
3
+ metadata.gz: e82b56fd6b5c8941104ade8ba8c8d683a1e01389
4
+ data.tar.gz: 60abfa4c732098cbf233088d00d84bed12d15546
5
5
  SHA512:
6
- metadata.gz: 8bcd5f84babf9bbbc494d276ba483765f160c968ccd16e069183eabdedcef256322ef7918044c465aaa707bfefc7a6fa74b7a86a02517887d4974f754f4f1083
7
- data.tar.gz: 2f474a94f96b6ec2c2b97fe91a8ed88c6e3eb86e8d355110e4696fc3e332d6061ee63df6c7c022ed650600df7d90b2d35f0e119966779aeb6f35849b58a450aa
6
+ metadata.gz: 4293f3d05fab0da40b7d0bcffbd5df4cd583ab00e61b158b7e5ecab247566cb6f99cb7623b1528df2c0bebb19e1882ee0d10a811693d061dc1738d21c6bfaeaf
7
+ data.tar.gz: c67664422e5d74242af736abc33909ba0626ae0ca52c002213d855c2c58a10c1b92ad7a66da21e227a84a779e092f512bd2eba69a17929e3da7cdde0984a2f22
data/docstache.gemspec CHANGED
@@ -19,4 +19,6 @@ Gem::Specification.new do |s|
19
19
  # specify any dependencies here; for example:
20
20
  s.add_runtime_dependency 'nokogiri', '~> 1.6'
21
21
  s.add_runtime_dependency 'rubyzip', '~> 1.1'
22
+
23
+ s.add_development_dependency 'rspec', '~> 3.1.0'
22
24
  end
@@ -0,0 +1,32 @@
1
+ module Docstache
2
+ class DataScope
3
+
4
+ def initialize(data, parent=EmptyDataScope.new)
5
+ @data = data
6
+ @parent = parent
7
+ end
8
+
9
+ def get(key, hash=@data, original_key=key)
10
+ tokens = key.split('.')
11
+ if tokens.length == 1
12
+ return hash.fetch(key.to_sym) { |key| @parent.get(original_key) }
13
+ elsif tokens.length > 1
14
+ key = tokens.shift
15
+ if hash.has_key?(key.to_sym)
16
+ subhash = hash.fetch(key.to_sym)
17
+ else
18
+ return @parent.get(original_key)
19
+ end
20
+ return get(tokens.join('.'), subhash, original_key)
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ class EmptyDataScope
27
+ def get(_)
28
+ return nil
29
+ end
30
+ end
31
+
32
+ end
@@ -2,7 +2,7 @@ module Docstache
2
2
  class Renderer
3
3
  def initialize(xml, data)
4
4
  @content = xml
5
- @data = data
5
+ @data = DataScope.new(data)
6
6
  end
7
7
 
8
8
  def render
@@ -42,7 +42,7 @@ module Docstache
42
42
  when end_nd.text.to_s
43
43
  out = []
44
44
  when /\{\{\#([a-zA-Z0-9_\.]+)\}\}/
45
- new_key = $1.to_sym
45
+ new_key = $1
46
46
  out += process_loop(nd, new_key, element)
47
47
  else
48
48
  new_node = nd.dup
@@ -71,26 +71,26 @@ module Docstache
71
71
 
72
72
  def process_loop(nd, key, data)
73
73
  out = []
74
- puts "Found Loop #{key.to_s}"
75
- end_row = extract_end_row(nd, key)
76
-
77
- if !data.has_key?(key)
78
- nil # Error in the data model
79
- return []
80
- elsif data[key].empty?
81
- remove_loop(nd, key) # No data to put in
82
- return []
83
- else # Actual loop to process
84
- data_set = data[key]
85
- puts "Expanding Rows for loop #{key.to_s}"
86
- puts "Data count is #{data_set.count}"
87
- puts "Data is #{data_set}"
88
-
89
- data_set.each do |element|
90
- out += expand_loop(nd, end_row, key, element)
91
- end
92
- return out
93
- end
74
+ # puts "Found Loop #{key.to_s}"
75
+ # end_row = extract_end_row(nd, key)
76
+ #
77
+ # if !data.has_key?(key)
78
+ # nil # Error in the data model
79
+ # return []
80
+ # elsif data[key].empty?
81
+ # remove_loop(nd, key) # No data to put in
82
+ # return []
83
+ # else # Actual loop to process
84
+ # data_set = data[key]
85
+ # puts "Expanding Rows for loop #{key.to_s}"
86
+ # puts "Data count is #{data_set.count}"
87
+ # puts "Data is #{data_set}"
88
+ #
89
+ # data_set.each do |element|
90
+ # out += expand_loop(nd, end_row, key, element)
91
+ # end
92
+ # return out
93
+ # end
94
94
  end
95
95
 
96
96
  def parse_content(elements, data=@data)
@@ -99,7 +99,7 @@ module Docstache
99
99
  when "tr"
100
100
  case nd.text.to_s
101
101
  when /\{\{\#([a-zA-Z0-9_\.]+)\}\}/
102
- key = $1.to_sym
102
+ key = $1
103
103
  # Get elements to add
104
104
  elements = process_loop(nd, key, data)
105
105
  # Add elements
@@ -133,9 +133,9 @@ module Docstache
133
133
 
134
134
  def subst_content(nd, data)
135
135
  inner = nd.inner_html
136
- keys = nd.text.scan(/\{\{([a-zA-Z0-9_\.]+)\}\}/).map(&:first).map(&:to_sym)
136
+ keys = nd.text.scan(/\{\{([a-zA-Z0-9_\.]+)\}\}/).map(&:first)
137
137
  keys.each do |key|
138
- value = data[key]
138
+ value = data.get(key)
139
139
  puts "Substituting {{#{key.to_s}}} with #{value}"
140
140
  inner.gsub!("{{#{key.to_s}}}", safe(value))
141
141
  end
@@ -1,3 +1,3 @@
1
1
  module Docstache
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/docstache.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'nokogiri'
2
2
  require 'zip'
3
3
 
4
+ require "docstache/data_scope"
4
5
  require "docstache/renderer"
5
6
  require "docstache/document"
6
7
 
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docstache::DataScope do
4
+ describe "#get" do
5
+ context "main body" do
6
+ let(:data_scope) { Docstache::DataScope.new({foo: "bar1", bar: {baz: "bar2", qux: {quux: "bar3"}}}) }
7
+ it "should resolve keys with no nesting" do
8
+ expect(data_scope.get('foo')).to eq("bar1")
9
+ end
10
+
11
+ it "should resolve nested keys" do
12
+ expect(data_scope.get('bar.baz')).to eq("bar2")
13
+ end
14
+
15
+ it "should resolve super nested keys" do
16
+ expect(data_scope.get('bar.qux.quux')).to eq("bar3")
17
+ end
18
+ end
19
+
20
+ context "loop" do
21
+ let(:parent_data_scope) { Docstache::DataScope.new({users: [{id: 1, name: "John Smith", brother: {id: 3, name: "Will Smith"}}], id: 2, foo: "bar", brother: {baz: "qux"}}) }
22
+ let(:data_scope) { Docstache::DataScope.new({id: 1, name: "John Smith", brother: {id: 3, name: "Will Smith"}}, parent_data_scope) }
23
+
24
+ it "should resolve keys with no nesting" do
25
+ expect(data_scope.get("id")).to eq(1)
26
+ end
27
+
28
+ it "should resolve nested keys" do
29
+ expect(data_scope.get("brother.id")).to eq(3)
30
+ end
31
+
32
+ it "should fall back to parent scope if key not found" do
33
+ expect(data_scope.get("foo")).to eq("bar")
34
+ end
35
+
36
+ it "should fall back to parent even during a partial match" do
37
+ expect(data_scope.get("brother.baz")).to eq("qux")
38
+ end
39
+
40
+ it "should return nil for no match" do
41
+ expect(data_scope.get("bat")).to be_nil
42
+ expect(data_scope.get("brother.qux")).to be_nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docstache::EmptyDataScope do
4
+ let(:empty_data_scope) { Docstache::EmptyDataScope.new }
5
+ describe '#get' do
6
+ it "should always return nil" do
7
+ expect(empty_data_scope.get('foo')).to be_nil
8
+ end
9
+ end
10
+ end
@@ -1,40 +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
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
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'fileutils'
2
- require 'docstache'
2
+ require_relative '../lib/docstache'
3
3
 
4
4
  SPEC_BASE_PATH = Pathname.new(File.expand_path(File.dirname(__FILE__)))
5
5
 
@@ -1,222 +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
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 CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docstache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Cosgrove
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-03 00:00:00.000000000 Z
11
+ date: 2014-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.6'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rubyzip
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.1.0
41
55
  description: Integrates data into MS Word docx template files. Processing supports
42
56
  loops and replacement of strings of data both outside and within loops.
43
57
  email:
@@ -46,16 +60,19 @@ executables: []
46
60
  extensions: []
47
61
  extra_rdoc_files: []
48
62
  files:
49
- - .gitignore
63
+ - ".gitignore"
50
64
  - Gemfile
51
65
  - LICENSE.txt
52
66
  - README.md
53
67
  - Rakefile
54
68
  - docstache.gemspec
55
69
  - lib/docstache.rb
70
+ - lib/docstache/data_scope.rb
56
71
  - lib/docstache/document.rb
57
72
  - lib/docstache/renderer.rb
58
73
  - lib/docstache/version.rb
74
+ - spec/data_scope_spec.rb
75
+ - spec/empty_data_scope_spec.rb
59
76
  - spec/example_input/ExampleTemplate.docx
60
77
  - spec/example_input/word/document.xml
61
78
  - spec/integration_spec.rb
@@ -71,21 +88,23 @@ require_paths:
71
88
  - lib
72
89
  required_ruby_version: !ruby/object:Gem::Requirement
73
90
  requirements:
74
- - - '>='
91
+ - - ">="
75
92
  - !ruby/object:Gem::Version
76
93
  version: '0'
77
94
  required_rubygems_version: !ruby/object:Gem::Requirement
78
95
  requirements:
79
- - - '>='
96
+ - - ">="
80
97
  - !ruby/object:Gem::Version
81
98
  version: '0'
82
99
  requirements: []
83
100
  rubyforge_project:
84
- rubygems_version: 2.0.14
101
+ rubygems_version: 2.2.2
85
102
  signing_key:
86
103
  specification_version: 4
87
104
  summary: Merges Hash of Data into Word docx template files using mustache syntax
88
105
  test_files:
106
+ - spec/data_scope_spec.rb
107
+ - spec/empty_data_scope_spec.rb
89
108
  - spec/example_input/ExampleTemplate.docx
90
109
  - spec/example_input/word/document.xml
91
110
  - spec/integration_spec.rb