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 +4 -4
- data/docstache.gemspec +2 -0
- data/lib/docstache/data_scope.rb +32 -0
- data/lib/docstache/renderer.rb +25 -25
- data/lib/docstache/version.rb +1 -1
- data/lib/docstache.rb +1 -0
- data/spec/data_scope_spec.rb +46 -0
- data/spec/empty_data_scope_spec.rb +10 -0
- data/spec/integration_spec.rb +40 -40
- data/spec/spec_helper.rb +1 -1
- data/spec/template_processor_spec.rb +222 -222
- metadata +29 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e82b56fd6b5c8941104ade8ba8c8d683a1e01389
|
|
4
|
+
data.tar.gz: 60abfa4c732098cbf233088d00d84bed12d15546
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4293f3d05fab0da40b7d0bcffbd5df4cd583ab00e61b158b7e5ecab247566cb6f99cb7623b1528df2c0bebb19e1882ee0d10a811693d061dc1738d21c6bfaeaf
|
|
7
|
+
data.tar.gz: c67664422e5d74242af736abc33909ba0626ae0ca52c002213d855c2c58a10c1b92ad7a66da21e227a84a779e092f512bd2eba69a17929e3da7cdde0984a2f22
|
data/docstache.gemspec
CHANGED
|
@@ -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
|
data/lib/docstache/renderer.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
elsif data[key].empty?
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
else # Actual loop to process
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
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)
|
|
136
|
+
keys = nd.text.scan(/\{\{([a-zA-Z0-9_\.]+)\}\}/).map(&:first)
|
|
137
137
|
keys.each do |key|
|
|
138
|
-
value = data
|
|
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
|
data/lib/docstache/version.rb
CHANGED
data/lib/docstache.rb
CHANGED
|
@@ -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
|
data/spec/integration_spec.rb
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'template_processor_spec'
|
|
3
|
-
|
|
4
|
-
describe 'integration test', integration: true do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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,222 +1,222 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'nokogiri'
|
|
3
|
-
|
|
4
|
-
module Docstache
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
describe Docstache::Render do
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
</xml>
|
|
105
|
-
EOF
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</xml>
|
|
132
|
-
EOF
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
140
|
-
<w:body>
|
|
141
|
-
|
|
142
|
-
</w:body>
|
|
143
|
-
</xml>
|
|
144
|
-
EOF
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
148
|
-
<w:body>
|
|
149
|
-
|
|
150
|
-
</w:body>
|
|
151
|
-
</xml>
|
|
152
|
-
EOF
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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 & A #1 floor')
|
|
61
|
+
# expect(out).to include('--> 201 <!--')
|
|
62
|
+
# expect(out).to include('<#Ai & Bo>')
|
|
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>&</p> #1 floor'
|
|
69
|
+
# out = parser.render(xml)
|
|
70
|
+
# expect(Nokogiri::XML.parse(out)).to be_xml
|
|
71
|
+
# expect(out).to include('23rd <p>&</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.
|
|
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-
|
|
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.
|
|
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
|