docstache 0.0.2 → 0.0.3

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.
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