sablon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +109 -0
- data/Rakefile +10 -0
- data/lib/sablon.rb +14 -0
- data/lib/sablon/operations.rb +61 -0
- data/lib/sablon/parser/mail_merge.rb +86 -0
- data/lib/sablon/processor.rb +135 -0
- data/lib/sablon/processor/section_properties.rb +34 -0
- data/lib/sablon/template.rb +35 -0
- data/lib/sablon/version.rb +3 -0
- data/sablon.gemspec +28 -0
- data/test/expression_test.rb +15 -0
- data/test/fixtures/sablon_sample.docx +0 -0
- data/test/fixtures/sablon_template.docx +0 -0
- data/test/mail_merge_parser_test.rb +131 -0
- data/test/processor_test.rb +697 -0
- data/test/sablon_test.rb +52 -0
- data/test/sandbox/.gitkeep +0 -0
- data/test/section_properties_test.rb +41 -0
- data/test/support/document_xml_helper.rb +26 -0
- data/test/test_helper.rb +13 -0
- metadata +163 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Sablon
|
2
|
+
class Processor
|
3
|
+
class SectionProperties
|
4
|
+
def self.from_document(document_xml)
|
5
|
+
new document_xml.at_xpath(".//w:sectPr")
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(properties_node)
|
9
|
+
@properties_node = properties_node
|
10
|
+
end
|
11
|
+
|
12
|
+
def start_page_number
|
13
|
+
pg_num_type && pg_num_type["w:start"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_page_number=(number)
|
17
|
+
find_or_add_pg_num_type["w:start"] = number
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def find_or_add_pg_num_type
|
22
|
+
pg_num_type || begin
|
23
|
+
node = Nokogiri::XML::Node.new "w:pgNumType", @properties_node.document
|
24
|
+
@properties_node.children.after node
|
25
|
+
node
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def pg_num_type
|
30
|
+
@pg_num_type ||= @properties_node.at_xpath(".//w:pgNumType")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Sablon
|
2
|
+
class Template
|
3
|
+
def initialize(path)
|
4
|
+
@path = path
|
5
|
+
end
|
6
|
+
|
7
|
+
# Same as +render_to_string+ but writes the processed template to +output_path+.
|
8
|
+
def render_to_file(output_path, context, properties = {})
|
9
|
+
File.open(output_path, 'w') do |f|
|
10
|
+
f.write render_to_string(context, properties)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Process the template. The +context+ hash will be available in the template.
|
15
|
+
def render_to_string(context, properties = {})
|
16
|
+
render(context, properties).string
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def render(context, properties = {})
|
21
|
+
Zip::OutputStream.write_buffer(StringIO.new) do |out|
|
22
|
+
Zip::File.open(@path).each do |entry|
|
23
|
+
entry_name = entry.name
|
24
|
+
out.put_next_entry(entry_name)
|
25
|
+
content = entry.get_input_stream.read
|
26
|
+
if entry_name == 'word/document.xml'
|
27
|
+
out.write(Processor.process(Nokogiri::XML(content), context, properties).to_xml)
|
28
|
+
else
|
29
|
+
out.write(content)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/sablon.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sablon/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sablon"
|
8
|
+
spec.version = Sablon::VERSION
|
9
|
+
spec.authors = ["Yves Senn"]
|
10
|
+
spec.email = ["yves.senn@gmail.com"]
|
11
|
+
spec.summary = %q{docx tempalte processor}
|
12
|
+
spec.description = %q{Sablon is a document template processor. At this time it works only with docx and MailMerge fields.}
|
13
|
+
spec.homepage = "http://github.com/senny/sablon"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'nokogiri'
|
22
|
+
spec.add_runtime_dependency 'rubyzip'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", ">= 1.6"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
27
|
+
spec.add_development_dependency "xml-simple"
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class ExpressionTest < Sablon::TestCase
|
5
|
+
def test_variable_expression
|
6
|
+
expr = Sablon::Expression.parse("first_name")
|
7
|
+
assert_equal "Jane", expr.evaluate({"first_name" => "Jane", "last_name" => "Doe"})
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_simple_method_call
|
11
|
+
user = OpenStruct.new(first_name: "Jack")
|
12
|
+
expr = Sablon::Expression.parse("user.first_name")
|
13
|
+
assert_equal "Jack", expr.evaluate({"user" => user})
|
14
|
+
end
|
15
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "test_helper"
|
3
|
+
require "support/document_xml_helper"
|
4
|
+
|
5
|
+
module MailMergeParser
|
6
|
+
module SharedBehavior
|
7
|
+
include DocumentXMLHelper
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
@parser = Sablon::Parser::MailMerge.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def fields
|
14
|
+
@document = xml
|
15
|
+
@parser.parse_fields(@document)
|
16
|
+
end
|
17
|
+
|
18
|
+
def body_xml
|
19
|
+
@document.search(".//w:body").children.map(&:to_xml).join.strip
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class FldSimpleTest < Sablon::TestCase
|
24
|
+
include SharedBehavior
|
25
|
+
|
26
|
+
def test_recognizes_expression
|
27
|
+
assert_equal ["=first_name"], fields.map(&:expression)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_replace
|
31
|
+
field = fields.first
|
32
|
+
field.replace("Hello")
|
33
|
+
assert_equal <<-body_xml.strip, body_xml
|
34
|
+
<w:r w:rsidR=\"004B49F0\">
|
35
|
+
<w:rPr><w:noProof/></w:rPr>
|
36
|
+
<w:t>Hello</w:t>
|
37
|
+
</w:r>
|
38
|
+
body_xml
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_replace_with_newlines
|
42
|
+
field = fields.first
|
43
|
+
field.replace("First\nSecond\n\nThird")
|
44
|
+
|
45
|
+
assert_equal <<-body_xml.strip, body_xml
|
46
|
+
<w:r w:rsidR=\"004B49F0\">
|
47
|
+
<w:rPr><w:noProof/></w:rPr>
|
48
|
+
<w:t>First</w:t><w:br/><w:t>Second</w:t><w:br/><w:br/><w:t>Third</w:t>
|
49
|
+
</w:r>
|
50
|
+
body_xml
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def xml
|
55
|
+
wrap(<<-xml)
|
56
|
+
<w:fldSimple w:instr=" MERGEFIELD =first_name \\* MERGEFORMAT ">
|
57
|
+
<w:r w:rsidR="004B49F0">
|
58
|
+
<w:rPr><w:noProof/></w:rPr>
|
59
|
+
<w:t>«=first_name»</w:t>
|
60
|
+
</w:r>
|
61
|
+
</w:fldSimple>
|
62
|
+
xml
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class FldCharTest < Sablon::TestCase
|
67
|
+
include SharedBehavior
|
68
|
+
|
69
|
+
def test_recognizes_expression
|
70
|
+
assert_equal ["=last_name"], fields.map(&:expression)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_replace
|
74
|
+
field = fields.first
|
75
|
+
field.replace("Hello")
|
76
|
+
assert_equal <<-body_xml.strip, body_xml
|
77
|
+
<w:r w:rsidR="004B49F0">
|
78
|
+
<w:rPr>
|
79
|
+
<w:b/>
|
80
|
+
<w:noProof/>
|
81
|
+
</w:rPr>
|
82
|
+
<w:t>Hello</w:t>
|
83
|
+
</w:r>
|
84
|
+
body_xml
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_replace_with_newlines
|
88
|
+
field = fields.first
|
89
|
+
field.replace("First\nSecond\n\nThird")
|
90
|
+
|
91
|
+
assert_equal <<-body_xml.strip, body_xml
|
92
|
+
<w:r w:rsidR="004B49F0">
|
93
|
+
<w:rPr>
|
94
|
+
<w:b/>
|
95
|
+
<w:noProof/>
|
96
|
+
</w:rPr>
|
97
|
+
<w:t>First</w:t><w:br/><w:t>Second</w:t><w:br/><w:br/><w:t>Third</w:t>
|
98
|
+
</w:r>
|
99
|
+
body_xml
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def xml
|
104
|
+
wrap(<<-xml)
|
105
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
106
|
+
<w:rPr><w:b/></w:rPr>
|
107
|
+
<w:fldChar w:fldCharType="begin"/>
|
108
|
+
</w:r>
|
109
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
110
|
+
<w:rPr><w:b/></w:rPr>
|
111
|
+
<w:instrText xml:space="preserve"> MERGEFIELD =last_name \\* MERGEFORMAT </w:instrText>
|
112
|
+
</w:r>
|
113
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
114
|
+
<w:rPr><w:b/></w:rPr>
|
115
|
+
<w:fldChar w:fldCharType="separate"/>
|
116
|
+
</w:r>
|
117
|
+
<w:r w:rsidR="004B49F0">
|
118
|
+
<w:rPr>
|
119
|
+
<w:b/>
|
120
|
+
<w:noProof/>
|
121
|
+
</w:rPr>
|
122
|
+
<w:t>«=last_name»</w:t>
|
123
|
+
</w:r>
|
124
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
125
|
+
<w:rPr><w:b/></w:rPr>
|
126
|
+
<w:fldChar w:fldCharType="end"/>
|
127
|
+
</w:r>
|
128
|
+
xml
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,697 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "test_helper"
|
3
|
+
require "support/document_xml_helper"
|
4
|
+
|
5
|
+
class ProcessorTest < Sablon::TestCase
|
6
|
+
include DocumentXMLHelper
|
7
|
+
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
@processor = Sablon::Processor
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_simple_field_replacement
|
14
|
+
result = process(<<-documentxml, {"first_name" => "Jack"})
|
15
|
+
<w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
|
16
|
+
<w:fldSimple w:instr=" MERGEFIELD =first_name \\* MERGEFORMAT ">
|
17
|
+
<w:r w:rsidR="004B49F0">
|
18
|
+
<w:rPr><w:noProof/></w:rPr>
|
19
|
+
<w:t>«=first_name»</w:t>
|
20
|
+
</w:r>
|
21
|
+
</w:fldSimple>
|
22
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
23
|
+
documentxml
|
24
|
+
|
25
|
+
|
26
|
+
assert_equal "Hello! My Name is Jack , nice to meet you.", text(result)
|
27
|
+
assert_xml_equal <<-document, result
|
28
|
+
<w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
|
29
|
+
<w:r w:rsidR="004B49F0">
|
30
|
+
<w:rPr><w:noProof/></w:rPr>
|
31
|
+
<w:t>Jack</w:t>
|
32
|
+
</w:r>
|
33
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
34
|
+
document
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_complex_field_replacement
|
38
|
+
result = process(<<-documentxml, {"last_name" => "Zane"})
|
39
|
+
<w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
|
40
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
41
|
+
<w:rPr>
|
42
|
+
<w:b/>
|
43
|
+
</w:rPr>
|
44
|
+
<w:fldChar w:fldCharType="begin"/>
|
45
|
+
</w:r>
|
46
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
47
|
+
<w:rPr>
|
48
|
+
<w:b/>
|
49
|
+
</w:rPr>
|
50
|
+
<w:instrText xml:space="preserve"> MERGEFIELD =last_name \\* MERGEFORMAT </w:instrText>
|
51
|
+
</w:r>
|
52
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
53
|
+
<w:rPr>
|
54
|
+
<w:b/>
|
55
|
+
</w:rPr>
|
56
|
+
<w:fldChar w:fldCharType="separate"/>
|
57
|
+
</w:r>
|
58
|
+
<w:r w:rsidR="004B49F0">
|
59
|
+
<w:rPr>
|
60
|
+
<w:b/>
|
61
|
+
<w:noProof/>
|
62
|
+
</w:rPr>
|
63
|
+
<w:t>«=last_name»</w:t>
|
64
|
+
</w:r>
|
65
|
+
<w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
|
66
|
+
<w:rPr>
|
67
|
+
<w:b/>
|
68
|
+
</w:rPr>
|
69
|
+
<w:fldChar w:fldCharType="end"/>
|
70
|
+
</w:r>
|
71
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
72
|
+
documentxml
|
73
|
+
|
74
|
+
assert_equal "Hello! My Name is Zane , nice to meet you.", text(result)
|
75
|
+
assert_xml_equal <<-document, result
|
76
|
+
<w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
|
77
|
+
<w:r w:rsidR="004B49F0">
|
78
|
+
<w:rPr>
|
79
|
+
<w:b/>
|
80
|
+
<w:noProof/>
|
81
|
+
</w:rPr>
|
82
|
+
<w:t>Zane</w:t>
|
83
|
+
</w:r>
|
84
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
85
|
+
document
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_complex_field_replacement_with_split_field
|
89
|
+
result = process(<<-documentxml, {"first_name" => "Daniel"})
|
90
|
+
<w:r>
|
91
|
+
<w:t xml:space="preserve">Hello! My Name is </w:t>
|
92
|
+
</w:r>
|
93
|
+
<w:r w:rsidR="003C4780">
|
94
|
+
<w:fldChar w:fldCharType="begin" />
|
95
|
+
</w:r>
|
96
|
+
<w:r w:rsidR="003C4780">
|
97
|
+
<w:instrText xml:space="preserve"> MERGEFIELD </w:instrText>
|
98
|
+
</w:r>
|
99
|
+
<w:r w:rsidR="003A4504">
|
100
|
+
<w:instrText>=</w:instrText>
|
101
|
+
</w:r>
|
102
|
+
<w:r w:rsidR="003C4780">
|
103
|
+
<w:instrText xml:space="preserve">first_name \\* MERGEFORMAT </w:instrText>
|
104
|
+
</w:r>
|
105
|
+
<w:r w:rsidR="003C4780">
|
106
|
+
<w:fldChar w:fldCharType="separate" />
|
107
|
+
</w:r>
|
108
|
+
<w:r w:rsidR="00441382">
|
109
|
+
<w:rPr>
|
110
|
+
<w:noProof />
|
111
|
+
</w:rPr>
|
112
|
+
<w:t>«=person.first_name»</w:t>
|
113
|
+
</w:r>
|
114
|
+
<w:r w:rsidR="003C4780">
|
115
|
+
<w:fldChar w:fldCharType="end" />
|
116
|
+
</w:r>
|
117
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
118
|
+
documentxml
|
119
|
+
|
120
|
+
assert_equal "Hello! My Name is Daniel , nice to meet you.", text(result)
|
121
|
+
assert_xml_equal <<-document, result
|
122
|
+
<w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
|
123
|
+
<w:r w:rsidR="00441382">
|
124
|
+
<w:rPr>
|
125
|
+
<w:noProof/>
|
126
|
+
</w:rPr>
|
127
|
+
<w:t>Daniel</w:t>
|
128
|
+
</w:r>
|
129
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
130
|
+
document
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_paragraph_block_replacement
|
134
|
+
item = Struct.new(:index, :label, :rating)
|
135
|
+
result = process(<<-document, {"technologies" => ["Ruby", "Rails"]})
|
136
|
+
<w:p w14:paraId="6CB29D92" w14:textId="164B70F4" w:rsidR="007F5CDE" w:rsidRDefault="007F5CDE" w:rsidP="007F5CDE">
|
137
|
+
<w:pPr>
|
138
|
+
<w:pStyle w:val="ListParagraph" />
|
139
|
+
<w:numPr>
|
140
|
+
<w:ilvl w:val="0" />
|
141
|
+
<w:numId w:val="1" />
|
142
|
+
</w:numPr>
|
143
|
+
</w:pPr>
|
144
|
+
<w:fldSimple w:instr=" MERGEFIELD technologies:each(technology) \\* MERGEFORMAT ">
|
145
|
+
<w:r>
|
146
|
+
<w:rPr>
|
147
|
+
<w:noProof />
|
148
|
+
</w:rPr>
|
149
|
+
<w:t>«technologies:each(technology)»</w:t>
|
150
|
+
</w:r>
|
151
|
+
</w:fldSimple>
|
152
|
+
</w:p>
|
153
|
+
<w:p w14:paraId="1081E316" w14:textId="3EAB5FDC" w:rsidR="00380EE8" w:rsidRDefault="00380EE8" w:rsidP="007F5CDE">
|
154
|
+
<w:pPr>
|
155
|
+
<w:pStyle w:val="ListParagraph" />
|
156
|
+
<w:numPr>
|
157
|
+
<w:ilvl w:val="0" />
|
158
|
+
<w:numId w:val="1" />
|
159
|
+
</w:numPr>
|
160
|
+
</w:pPr>
|
161
|
+
<w:r>
|
162
|
+
<w:fldChar w:fldCharType="begin" />
|
163
|
+
</w:r>
|
164
|
+
<w:r>
|
165
|
+
<w:instrText xml:space="preserve"> </w:instrText>
|
166
|
+
</w:r>
|
167
|
+
<w:r w:rsidR="009F01DA">
|
168
|
+
<w:instrText>MERGEFIELD =technology</w:instrText>
|
169
|
+
</w:r>
|
170
|
+
<w:r>
|
171
|
+
<w:instrText xml:space="preserve"> \\* MERGEFORMAT </w:instrText>
|
172
|
+
</w:r>
|
173
|
+
<w:r>
|
174
|
+
<w:fldChar w:fldCharType="separate" />
|
175
|
+
</w:r>
|
176
|
+
<w:r w:rsidR="009F01DA">
|
177
|
+
<w:rPr>
|
178
|
+
<w:noProof />
|
179
|
+
</w:rPr>
|
180
|
+
<w:t>«=technology»</w:t>
|
181
|
+
</w:r>
|
182
|
+
<w:r>
|
183
|
+
<w:fldChar w:fldCharType="end" />
|
184
|
+
</w:r>
|
185
|
+
</w:p>
|
186
|
+
<w:p w14:paraId="7F936853" w14:textId="078377AD" w:rsidR="00380EE8" w:rsidRPr="007F5CDE" w:rsidRDefault="00380EE8" w:rsidP="007F5CDE">
|
187
|
+
<w:pPr>
|
188
|
+
<w:pStyle w:val="ListParagraph" />
|
189
|
+
<w:numPr>
|
190
|
+
<w:ilvl w:val="0" />
|
191
|
+
<w:numId w:val="1" />
|
192
|
+
</w:numPr>
|
193
|
+
</w:pPr>
|
194
|
+
<w:fldSimple w:instr=" MERGEFIELD technologies:endEach \\* MERGEFORMAT ">
|
195
|
+
<w:r>
|
196
|
+
<w:rPr>
|
197
|
+
<w:noProof />
|
198
|
+
</w:rPr>
|
199
|
+
<w:t>«technologies:endEach»</w:t>
|
200
|
+
</w:r>
|
201
|
+
</w:fldSimple>
|
202
|
+
</w:p>
|
203
|
+
document
|
204
|
+
|
205
|
+
assert_equal "Ruby Rails", text(result)
|
206
|
+
assert_xml_equal <<-document, result
|
207
|
+
<w:p w14:paraId="1081E316" w14:textId="3EAB5FDC" w:rsidR="00380EE8" w:rsidRDefault="00380EE8" w:rsidP="007F5CDE">
|
208
|
+
<w:pPr>
|
209
|
+
<w:pStyle w:val="ListParagraph"/>
|
210
|
+
<w:numPr>
|
211
|
+
<w:ilvl w:val="0"/>
|
212
|
+
<w:numId w:val="1"/>
|
213
|
+
</w:numPr>
|
214
|
+
</w:pPr>
|
215
|
+
<w:r w:rsidR="009F01DA">
|
216
|
+
<w:rPr>
|
217
|
+
<w:noProof/>
|
218
|
+
</w:rPr>
|
219
|
+
<w:t>Ruby</w:t>
|
220
|
+
</w:r>
|
221
|
+
</w:p><w:p w14:paraId="1081E316" w14:textId="3EAB5FDC" w:rsidR="00380EE8" w:rsidRDefault="00380EE8" w:rsidP="007F5CDE">
|
222
|
+
<w:pPr>
|
223
|
+
<w:pStyle w:val="ListParagraph"/>
|
224
|
+
<w:numPr>
|
225
|
+
<w:ilvl w:val="0"/>
|
226
|
+
<w:numId w:val="1"/>
|
227
|
+
</w:numPr>
|
228
|
+
</w:pPr>
|
229
|
+
<w:r w:rsidR="009F01DA">
|
230
|
+
<w:rPr>
|
231
|
+
<w:noProof/>
|
232
|
+
</w:rPr>
|
233
|
+
<w:t>Rails</w:t>
|
234
|
+
</w:r>
|
235
|
+
</w:p>
|
236
|
+
document
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_single_row_table_loop
|
240
|
+
item = Struct.new(:index, :label, :rating)
|
241
|
+
result = process(<<-document, {"items" => [item.new("1.", "Milk", "***"), item.new("2.", "Sugar", "**")]})
|
242
|
+
<w:tbl>
|
243
|
+
<w:tblPr>
|
244
|
+
<w:tblStyle w:val="TableGrid"/>
|
245
|
+
<w:tblW w:w="0" w:type="auto"/>
|
246
|
+
<w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
|
247
|
+
</w:tblPr>
|
248
|
+
<w:tblGrid>
|
249
|
+
<w:gridCol w:w="2202"/>
|
250
|
+
<w:gridCol w:w="4285"/>
|
251
|
+
<w:gridCol w:w="2029"/>
|
252
|
+
</w:tblGrid>
|
253
|
+
<w:tr w:rsidR="00757DAD" w14:paraId="229B7A39" w14:textId="77777777" w:rsidTr="006333C3">
|
254
|
+
<w:tc>
|
255
|
+
<w:tcPr>
|
256
|
+
<w:tcW w:w="2202" w:type="dxa"/>
|
257
|
+
</w:tcPr>
|
258
|
+
<w:p w14:paraId="3D472BF1" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
259
|
+
<w:r>
|
260
|
+
<w:fldChar w:fldCharType="begin"/>
|
261
|
+
</w:r>
|
262
|
+
<w:r>
|
263
|
+
<w:instrText xml:space="preserve"> MERGEFIELD items:each(item) \\* MERGEFORMAT </w:instrText>
|
264
|
+
</w:r>
|
265
|
+
<w:r>
|
266
|
+
<w:fldChar w:fldCharType="separate"/>
|
267
|
+
</w:r>
|
268
|
+
<w:r>
|
269
|
+
<w:rPr>
|
270
|
+
<w:noProof/>
|
271
|
+
</w:rPr>
|
272
|
+
<w:t>«items:each(item)»</w:t>
|
273
|
+
</w:r>
|
274
|
+
<w:r>
|
275
|
+
<w:fldChar w:fldCharType="end"/>
|
276
|
+
</w:r>
|
277
|
+
</w:p>
|
278
|
+
</w:tc>
|
279
|
+
<w:tc>
|
280
|
+
<w:tcPr>
|
281
|
+
<w:tcW w:w="4285" w:type="dxa"/>
|
282
|
+
</w:tcPr>
|
283
|
+
<w:p w14:paraId="6E6D8DB2" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3"/>
|
284
|
+
</w:tc>
|
285
|
+
<w:tc>
|
286
|
+
<w:tcPr>
|
287
|
+
<w:tcW w:w="2029" w:type="dxa"/>
|
288
|
+
</w:tcPr>
|
289
|
+
<w:p w14:paraId="7BE1DB00" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3"/>
|
290
|
+
</w:tc>
|
291
|
+
</w:tr>
|
292
|
+
<w:tr w:rsidR="00757DAD" w14:paraId="1BD2E50A" w14:textId="77777777" w:rsidTr="006333C3">
|
293
|
+
<w:tc>
|
294
|
+
<w:tcPr>
|
295
|
+
<w:tcW w:w="2202" w:type="dxa"/>
|
296
|
+
</w:tcPr>
|
297
|
+
<w:p w14:paraId="41ACB3D9" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
298
|
+
<w:r>
|
299
|
+
<w:fldChar w:fldCharType="begin"/>
|
300
|
+
</w:r>
|
301
|
+
<w:r>
|
302
|
+
<w:instrText xml:space="preserve"> MERGEFIELD =item.index \\* MERGEFORMAT </w:instrText>
|
303
|
+
</w:r>
|
304
|
+
<w:r>
|
305
|
+
<w:fldChar w:fldCharType="separate"/>
|
306
|
+
</w:r>
|
307
|
+
<w:r>
|
308
|
+
<w:rPr>
|
309
|
+
<w:noProof/>
|
310
|
+
</w:rPr>
|
311
|
+
<w:t>«=item.index»</w:t>
|
312
|
+
</w:r>
|
313
|
+
<w:r>
|
314
|
+
<w:fldChar w:fldCharType="end"/>
|
315
|
+
</w:r>
|
316
|
+
</w:p>
|
317
|
+
</w:tc>
|
318
|
+
<w:tc>
|
319
|
+
<w:tcPr>
|
320
|
+
<w:tcW w:w="4285" w:type="dxa"/>
|
321
|
+
</w:tcPr>
|
322
|
+
<w:p w14:paraId="197C6F31" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
323
|
+
<w:r>
|
324
|
+
<w:fldChar w:fldCharType="begin"/>
|
325
|
+
</w:r>
|
326
|
+
<w:r>
|
327
|
+
<w:instrText xml:space="preserve"> MERGEFIELD =item.label \\* MERGEFORMAT </w:instrText>
|
328
|
+
</w:r>
|
329
|
+
<w:r>
|
330
|
+
<w:fldChar w:fldCharType="separate"/>
|
331
|
+
</w:r>
|
332
|
+
<w:r>
|
333
|
+
<w:rPr>
|
334
|
+
<w:noProof/>
|
335
|
+
</w:rPr>
|
336
|
+
<w:t>«=item.label»</w:t>
|
337
|
+
</w:r>
|
338
|
+
<w:r>
|
339
|
+
<w:fldChar w:fldCharType="end"/>
|
340
|
+
</w:r>
|
341
|
+
</w:p>
|
342
|
+
</w:tc>
|
343
|
+
<w:tc>
|
344
|
+
<w:tcPr>
|
345
|
+
<w:tcW w:w="2029" w:type="dxa"/>
|
346
|
+
</w:tcPr>
|
347
|
+
<w:p w14:paraId="55C258BB" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
348
|
+
<w:r>
|
349
|
+
<w:fldChar w:fldCharType="begin"/>
|
350
|
+
</w:r>
|
351
|
+
<w:r>
|
352
|
+
<w:instrText xml:space="preserve"> MERGEFIELD =item.rating \\* MERGEFORMAT </w:instrText>
|
353
|
+
</w:r>
|
354
|
+
<w:r>
|
355
|
+
<w:fldChar w:fldCharType="separate"/>
|
356
|
+
</w:r>
|
357
|
+
<w:r>
|
358
|
+
<w:rPr>
|
359
|
+
<w:noProof/>
|
360
|
+
</w:rPr>
|
361
|
+
<w:t>«=item.rating»</w:t>
|
362
|
+
</w:r>
|
363
|
+
<w:r>
|
364
|
+
<w:fldChar w:fldCharType="end"/>
|
365
|
+
</w:r>
|
366
|
+
</w:p>
|
367
|
+
</w:tc>
|
368
|
+
</w:tr>
|
369
|
+
<w:tr w:rsidR="00757DAD" w14:paraId="2D3C09BC" w14:textId="77777777" w:rsidTr="006333C3">
|
370
|
+
<w:tc>
|
371
|
+
<w:tcPr>
|
372
|
+
<w:tcW w:w="2202" w:type="dxa"/>
|
373
|
+
</w:tcPr>
|
374
|
+
<w:p w14:paraId="04A961B7" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
375
|
+
<w:r>
|
376
|
+
<w:fldChar w:fldCharType="begin"/>
|
377
|
+
</w:r>
|
378
|
+
<w:r>
|
379
|
+
<w:instrText xml:space="preserve"> MERGEFIELD items:endEach \\* MERGEFORMAT </w:instrText>
|
380
|
+
</w:r>
|
381
|
+
<w:r>
|
382
|
+
<w:fldChar w:fldCharType="separate"/>
|
383
|
+
</w:r>
|
384
|
+
<w:r>
|
385
|
+
<w:rPr>
|
386
|
+
<w:noProof/>
|
387
|
+
</w:rPr>
|
388
|
+
<w:t>«items:endEach»</w:t>
|
389
|
+
</w:r>
|
390
|
+
<w:r>
|
391
|
+
<w:fldChar w:fldCharType="end"/>
|
392
|
+
</w:r>
|
393
|
+
</w:p>
|
394
|
+
</w:tc>
|
395
|
+
<w:tc>
|
396
|
+
<w:tcPr>
|
397
|
+
<w:tcW w:w="4285" w:type="dxa"/>
|
398
|
+
</w:tcPr>
|
399
|
+
<w:p w14:paraId="71165BFB" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3"/>
|
400
|
+
</w:tc>
|
401
|
+
<w:tc>
|
402
|
+
<w:tcPr>
|
403
|
+
<w:tcW w:w="2029" w:type="dxa"/>
|
404
|
+
</w:tcPr>
|
405
|
+
<w:p w14:paraId="01D3965C" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3"/>
|
406
|
+
</w:tc>
|
407
|
+
</w:tr>
|
408
|
+
</w:tbl>
|
409
|
+
document
|
410
|
+
|
411
|
+
assert_xml_equal <<-document, result
|
412
|
+
<w:tbl>
|
413
|
+
<w:tblPr>
|
414
|
+
<w:tblStyle w:val="TableGrid"/>
|
415
|
+
<w:tblW w:w="0" w:type="auto"/>
|
416
|
+
<w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="1" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/>
|
417
|
+
</w:tblPr>
|
418
|
+
<w:tblGrid>
|
419
|
+
<w:gridCol w:w="2202"/>
|
420
|
+
<w:gridCol w:w="4285"/>
|
421
|
+
<w:gridCol w:w="2029"/>
|
422
|
+
</w:tblGrid>
|
423
|
+
<w:tr w:rsidR="00757DAD" w14:paraId="1BD2E50A" w14:textId="77777777" w:rsidTr="006333C3">
|
424
|
+
<w:tc>
|
425
|
+
<w:tcPr>
|
426
|
+
<w:tcW w:w="2202" w:type="dxa"/>
|
427
|
+
</w:tcPr>
|
428
|
+
<w:p w14:paraId="41ACB3D9" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
429
|
+
<w:r>
|
430
|
+
<w:rPr>
|
431
|
+
<w:noProof/>
|
432
|
+
</w:rPr>
|
433
|
+
<w:t>1.</w:t>
|
434
|
+
</w:r>
|
435
|
+
</w:p>
|
436
|
+
</w:tc>
|
437
|
+
<w:tc>
|
438
|
+
<w:tcPr>
|
439
|
+
<w:tcW w:w="4285" w:type="dxa"/>
|
440
|
+
</w:tcPr>
|
441
|
+
<w:p w14:paraId="197C6F31" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
442
|
+
<w:r>
|
443
|
+
<w:rPr>
|
444
|
+
<w:noProof/>
|
445
|
+
</w:rPr>
|
446
|
+
<w:t>Milk</w:t>
|
447
|
+
</w:r>
|
448
|
+
</w:p>
|
449
|
+
</w:tc>
|
450
|
+
<w:tc>
|
451
|
+
<w:tcPr>
|
452
|
+
<w:tcW w:w="2029" w:type="dxa"/>
|
453
|
+
</w:tcPr>
|
454
|
+
<w:p w14:paraId="55C258BB" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
455
|
+
<w:r>
|
456
|
+
<w:rPr>
|
457
|
+
<w:noProof/>
|
458
|
+
</w:rPr>
|
459
|
+
<w:t>***</w:t>
|
460
|
+
</w:r>
|
461
|
+
</w:p>
|
462
|
+
</w:tc>
|
463
|
+
</w:tr><w:tr w:rsidR="00757DAD" w14:paraId="1BD2E50A" w14:textId="77777777" w:rsidTr="006333C3">
|
464
|
+
<w:tc>
|
465
|
+
<w:tcPr>
|
466
|
+
<w:tcW w:w="2202" w:type="dxa"/>
|
467
|
+
</w:tcPr>
|
468
|
+
<w:p w14:paraId="41ACB3D9" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
469
|
+
<w:r>
|
470
|
+
<w:rPr>
|
471
|
+
<w:noProof/>
|
472
|
+
</w:rPr>
|
473
|
+
<w:t>2.</w:t>
|
474
|
+
</w:r>
|
475
|
+
</w:p>
|
476
|
+
</w:tc>
|
477
|
+
<w:tc>
|
478
|
+
<w:tcPr>
|
479
|
+
<w:tcW w:w="4285" w:type="dxa"/>
|
480
|
+
</w:tcPr>
|
481
|
+
<w:p w14:paraId="197C6F31" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
482
|
+
<w:r>
|
483
|
+
<w:rPr>
|
484
|
+
<w:noProof/>
|
485
|
+
</w:rPr>
|
486
|
+
<w:t>Sugar</w:t>
|
487
|
+
</w:r>
|
488
|
+
</w:p>
|
489
|
+
</w:tc>
|
490
|
+
<w:tc>
|
491
|
+
<w:tcPr>
|
492
|
+
<w:tcW w:w="2029" w:type="dxa"/>
|
493
|
+
</w:tcPr>
|
494
|
+
<w:p w14:paraId="55C258BB" w14:textId="77777777" w:rsidR="00757DAD" w:rsidRDefault="00757DAD" w:rsidP="006333C3">
|
495
|
+
<w:r>
|
496
|
+
<w:rPr>
|
497
|
+
<w:noProof/>
|
498
|
+
</w:rPr>
|
499
|
+
<w:t>**</w:t>
|
500
|
+
</w:r>
|
501
|
+
</w:p>
|
502
|
+
</w:tc>
|
503
|
+
</w:tr>
|
504
|
+
</w:tbl>
|
505
|
+
|
506
|
+
document
|
507
|
+
end
|
508
|
+
|
509
|
+
def test_multi_row_table_loop
|
510
|
+
item = Struct.new(:index, :label, :body)
|
511
|
+
context = {"foods" => [item.new("1.", "Milk", "Milk is a white liquid."),
|
512
|
+
item.new("2.", "Sugar", "Sugar is the generalized name for carbohydrates.")]}
|
513
|
+
result = process(<<-document, context)
|
514
|
+
<w:tbl>
|
515
|
+
<w:tr w:rsidR="00F23752" w14:paraId="3FF89DEC" w14:textId="77777777" w:rsidTr="00213ACD">
|
516
|
+
<w:tc>
|
517
|
+
<w:tcPr>
|
518
|
+
<w:tcW w:w="2235" w:type="dxa" />
|
519
|
+
<w:shd w:val="clear" w:color="auto" w:fill="auto" />
|
520
|
+
</w:tcPr>
|
521
|
+
<w:p w14:paraId="7630A6C6" w14:textId="699D0C71" w:rsidR="00F23752" w:rsidRDefault="00F23752" w:rsidP="003F16E3">
|
522
|
+
<w:fldSimple w:instr=" MERGEFIELD foods:each(food) \\* MERGEFORMAT ">
|
523
|
+
<w:r w:rsidR="00213ACD">
|
524
|
+
<w:rPr>
|
525
|
+
<w:noProof />
|
526
|
+
</w:rPr>
|
527
|
+
<w:t>«foods:each(food)»</w:t>
|
528
|
+
</w:r>
|
529
|
+
</w:fldSimple>
|
530
|
+
</w:p>
|
531
|
+
</w:tc>
|
532
|
+
<w:tc>
|
533
|
+
<w:tcPr>
|
534
|
+
<w:tcW w:w="6287" w:type="dxa" />
|
535
|
+
<w:shd w:val="clear" w:color="auto" w:fill="auto" />
|
536
|
+
</w:tcPr>
|
537
|
+
<w:p w14:paraId="437AFC74" w14:textId="77777777" w:rsidR="00F23752" w:rsidRDefault="00F23752" w:rsidP="003F16E3" />
|
538
|
+
</w:tc>
|
539
|
+
</w:tr>
|
540
|
+
<w:tr w:rsidR="00F23752" w14:paraId="320AE02B" w14:textId="77777777" w:rsidTr="00213ACD">
|
541
|
+
<w:tc>
|
542
|
+
<w:tcPr>
|
543
|
+
<w:tcW w:w="2235" w:type="dxa" />
|
544
|
+
<w:shd w:val="clear" w:color="auto" w:fill="8DB3E2" w:themeFill="text2" w:themeFillTint="66" />
|
545
|
+
</w:tcPr>
|
546
|
+
<w:p w14:paraId="3FCF3855" w14:textId="38FA7F3B" w:rsidR="00F23752" w:rsidRDefault="00F23752" w:rsidP="00F23752">
|
547
|
+
<w:fldSimple w:instr=" MERGEFIELD =food.index \\* MERGEFORMAT ">
|
548
|
+
<w:r w:rsidR="00213ACD">
|
549
|
+
<w:rPr>
|
550
|
+
<w:noProof />
|
551
|
+
</w:rPr>
|
552
|
+
<w:t>«=food.index»</w:t>
|
553
|
+
</w:r>
|
554
|
+
</w:fldSimple>
|
555
|
+
</w:p>
|
556
|
+
</w:tc>
|
557
|
+
<w:tc>
|
558
|
+
<w:tcPr>
|
559
|
+
<w:tcW w:w="6287" w:type="dxa" />
|
560
|
+
<w:shd w:val="clear" w:color="auto" w:fill="8DB3E2" w:themeFill="text2" w:themeFillTint="66" />
|
561
|
+
</w:tcPr>
|
562
|
+
<w:p w14:paraId="0BB0E74E" w14:textId="4FA0D282" w:rsidR="00F23752" w:rsidRPr="00F576DA" w:rsidRDefault="00F23752" w:rsidP="00F23752">
|
563
|
+
<w:r w:rsidRPr="00F576DA">
|
564
|
+
<w:fldChar w:fldCharType="begin" />
|
565
|
+
</w:r>
|
566
|
+
<w:r w:rsidRPr="00F576DA">
|
567
|
+
<w:instrText xml:space="preserve"> MERGEFIELD =</w:instrText>
|
568
|
+
</w:r>
|
569
|
+
<w:r>
|
570
|
+
<w:instrText>food</w:instrText>
|
571
|
+
</w:r>
|
572
|
+
<w:r w:rsidRPr="00F576DA">
|
573
|
+
<w:instrText xml:space="preserve">.label \\* MERGEFORMAT </w:instrText>
|
574
|
+
</w:r>
|
575
|
+
<w:r w:rsidRPr="00F576DA">
|
576
|
+
<w:fldChar w:fldCharType="separate" />
|
577
|
+
</w:r>
|
578
|
+
<w:r w:rsidR="00213ACD">
|
579
|
+
<w:rPr>
|
580
|
+
<w:rFonts w:ascii="Comic Sans MS" w:hAnsi="Comic Sans MS" />
|
581
|
+
<w:noProof />
|
582
|
+
</w:rPr>
|
583
|
+
<w:t>«=food.label»</w:t>
|
584
|
+
</w:r>
|
585
|
+
<w:r w:rsidRPr="00F576DA">
|
586
|
+
<w:fldChar w:fldCharType="end" />
|
587
|
+
</w:r>
|
588
|
+
</w:p>
|
589
|
+
</w:tc>
|
590
|
+
</w:tr>
|
591
|
+
<w:tr w:rsidR="00213ACD" w14:paraId="1EA188ED" w14:textId="77777777" w:rsidTr="00213ACD">
|
592
|
+
<w:tc>
|
593
|
+
<w:tcPr>
|
594
|
+
<w:tcW w:w="8522" w:type="dxa" />
|
595
|
+
<w:gridSpan w:val="2" />
|
596
|
+
<w:shd w:val="clear" w:color="auto" w:fill="auto" />
|
597
|
+
</w:tcPr>
|
598
|
+
<w:p w14:paraId="3E9FF163" w14:textId="0F37CDFB" w:rsidR="00213ACD" w:rsidRDefault="00213ACD" w:rsidP="003F16E3">
|
599
|
+
<w:fldSimple w:instr=" MERGEFIELD =food.body \\* MERGEFORMAT ">
|
600
|
+
<w:r>
|
601
|
+
<w:rPr>
|
602
|
+
<w:noProof />
|
603
|
+
</w:rPr>
|
604
|
+
<w:t>«=food.body»</w:t>
|
605
|
+
</w:r>
|
606
|
+
</w:fldSimple>
|
607
|
+
</w:p>
|
608
|
+
</w:tc>
|
609
|
+
</w:tr>
|
610
|
+
<w:tr w:rsidR="00213ACD" w14:paraId="34315A41" w14:textId="77777777" w:rsidTr="00213ACD">
|
611
|
+
<w:tc>
|
612
|
+
<w:tcPr>
|
613
|
+
<w:tcW w:w="2235" w:type="dxa" />
|
614
|
+
<w:shd w:val="clear" w:color="auto" w:fill="auto" />
|
615
|
+
</w:tcPr>
|
616
|
+
<w:p w14:paraId="1CA83F76" w14:textId="2622C490" w:rsidR="00213ACD" w:rsidRDefault="00213ACD" w:rsidP="003F16E3">
|
617
|
+
<w:r>
|
618
|
+
<w:fldChar w:fldCharType="begin" />
|
619
|
+
</w:r>
|
620
|
+
<w:r>
|
621
|
+
<w:instrText xml:space="preserve"> MERGEFIELD foods:endEach \\* MERGEFORMAT </w:instrText>
|
622
|
+
</w:r>
|
623
|
+
<w:r>
|
624
|
+
<w:fldChar w:fldCharType="separate" />
|
625
|
+
</w:r>
|
626
|
+
<w:r>
|
627
|
+
<w:rPr>
|
628
|
+
<w:noProof />
|
629
|
+
</w:rPr>
|
630
|
+
<w:t>«foods:endEach»</w:t>
|
631
|
+
</w:r>
|
632
|
+
<w:r>
|
633
|
+
<w:fldChar w:fldCharType="end" />
|
634
|
+
</w:r>
|
635
|
+
</w:p>
|
636
|
+
</w:tc>
|
637
|
+
<w:tc>
|
638
|
+
<w:tcPr>
|
639
|
+
<w:tcW w:w="6287" w:type="dxa" />
|
640
|
+
<w:shd w:val="clear" w:color="auto" w:fill="auto" />
|
641
|
+
</w:tcPr>
|
642
|
+
<w:p w14:paraId="7D976602" w14:textId="77777777" w:rsidR="00213ACD" w:rsidRDefault="00213ACD" w:rsidP="003F16E3" />
|
643
|
+
</w:tc>
|
644
|
+
</w:tr>
|
645
|
+
</w:tbl>
|
646
|
+
document
|
647
|
+
|
648
|
+
assert_equal "1. Milk Milk is a white liquid. 2. Sugar Sugar is the generalized name for carbohydrates.", text(result)
|
649
|
+
end
|
650
|
+
|
651
|
+
def test_conditional
|
652
|
+
document = <<-documentxml
|
653
|
+
<w:r><w:t xml:space="preserve">Anthony</w:t></w:r>
|
654
|
+
<w:p>
|
655
|
+
<w:fldSimple w:instr=" MERGEFIELD middle_name:if \\* MERGEFORMAT ">
|
656
|
+
<w:r>
|
657
|
+
<w:rPr>
|
658
|
+
<w:noProof/>
|
659
|
+
</w:rPr>
|
660
|
+
<w:t>«middle_name:if»</w:t>
|
661
|
+
</w:r>
|
662
|
+
</w:fldSimple>
|
663
|
+
</w:p>
|
664
|
+
<w:p>
|
665
|
+
<w:fldSimple w:instr=" MERGEFIELD =middle_name \\* MERGEFORMAT ">
|
666
|
+
<w:r>
|
667
|
+
<w:rPr>
|
668
|
+
<w:noProof/>
|
669
|
+
</w:rPr>
|
670
|
+
<w:t>«=middle_name»</w:t>
|
671
|
+
</w:r>
|
672
|
+
</w:fldSimple>
|
673
|
+
</w:p>
|
674
|
+
<w:p>
|
675
|
+
<w:fldSimple w:instr=" MERGEFIELD middle_name:endIf \\* MERGEFORMAT ">
|
676
|
+
<w:r>
|
677
|
+
<w:rPr>
|
678
|
+
<w:noProof/>
|
679
|
+
</w:rPr>
|
680
|
+
<w:t>«middle_name:endIf»</w:t>
|
681
|
+
</w:r>
|
682
|
+
</w:fldSimple>
|
683
|
+
</w:p>
|
684
|
+
<w:r><w:t xml:space="preserve">Hall</w:t></w:r>
|
685
|
+
documentxml
|
686
|
+
result = process(document, {"middle_name" => "Michael"})
|
687
|
+
assert_equal "Anthony Michael Hall", text(result)
|
688
|
+
|
689
|
+
result = process(document, {"middle_name" => nil})
|
690
|
+
assert_equal "Anthony Hall", text(result)
|
691
|
+
end
|
692
|
+
|
693
|
+
private
|
694
|
+
def process(document, context)
|
695
|
+
@processor.process(wrap(document), context).to_xml
|
696
|
+
end
|
697
|
+
end
|