sablon 0.3.2 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +3 -9
- data/Gemfile.lock +14 -12
- data/lib/sablon/content.rb +10 -5
- data/lib/sablon/context.rb +7 -4
- data/lib/sablon/processor/document/blocks.rb +40 -4
- data/lib/sablon/template.rb +1 -1
- data/lib/sablon/test/assertions.rb +60 -0
- data/lib/sablon/version.rb +1 -1
- data/misc/TEMPLATE.md +26 -10
- data/sablon.gemspec +2 -2
- data/test/configuration_test.rb +3 -2
- data/test/context_test.rb +22 -0
- data/test/fixtures/conditionals_sample.docx +0 -0
- data/test/fixtures/conditionals_template.docx +0 -0
- data/test/fixtures/images/svg_sample.svg +6 -0
- data/test/fixtures/images_sample.docx +0 -0
- data/test/fixtures/images_template.docx +0 -0
- data/test/fixtures/svg_images_sample.docx +0 -0
- data/test/fixtures/svg_images_template.docx +0 -0
- data/test/html/ast_builder_test.rb +1 -1
- data/test/sablon_test.rb +25 -2
- data/test/test_helper.rb +3 -56
- metadata +17 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 720cffa05ff6bb8247f98dcf41bc071337261dcea325efe2b7f711bc728b342d
|
4
|
+
data.tar.gz: 1a75ef6694f0a608c5d5fc2ceec212017bf2ff0a547970291b1de86ef44f436c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43becf98304baf22069a66dd2f7317f78acb66047523129f9f108d83b682b3bcbdb154142a536bc629d3c71a56edeef3c1b1aac81004fbc71946499d5587fb98
|
7
|
+
data.tar.gz: ee6d21a857e7ae944bc173663868b7987b60c96752e2fae6d303b96ded3208f7e041cc8f38018547a8f9b469a9574677a237666a72cffd1a60ba7113480fa47b
|
data/.travis.yml
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
+
before_install:
|
2
|
+
- gem install bundler:1.17.2
|
1
3
|
language: ruby
|
2
4
|
rvm:
|
3
|
-
- 2.2
|
4
|
-
- 2.3
|
5
|
-
- 2.4
|
6
|
-
- 2.5
|
7
5
|
- 2.6
|
8
|
-
|
9
|
-
before_install:
|
10
|
-
# This is a workaround for a problem with a specific version of bundler and
|
11
|
-
# rubygems
|
12
|
-
- gem update --system
|
6
|
+
- 2.7
|
data/Gemfile.lock
CHANGED
@@ -1,30 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sablon (0.
|
4
|
+
sablon (0.4.1)
|
5
5
|
nokogiri (>= 1.8.5)
|
6
|
-
rubyzip (>= 1.
|
6
|
+
rubyzip (>= 1.3.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
rake (
|
16
|
-
|
17
|
-
|
11
|
+
minitest (5.20.0)
|
12
|
+
nokogiri (1.16.5-arm64-darwin)
|
13
|
+
racc (~> 1.4)
|
14
|
+
racc (1.8.0)
|
15
|
+
rake (13.1.0)
|
16
|
+
rexml (3.2.6)
|
17
|
+
rubyzip (2.3.2)
|
18
|
+
xml-simple (1.1.9)
|
19
|
+
rexml
|
18
20
|
|
19
21
|
PLATFORMS
|
20
|
-
|
22
|
+
arm64-darwin-23
|
21
23
|
|
22
24
|
DEPENDENCIES
|
23
25
|
bundler (>= 1.6)
|
24
26
|
minitest (~> 5.4)
|
25
|
-
rake (~>
|
27
|
+
rake (~> 13.0)
|
26
28
|
sablon!
|
27
29
|
xml-simple
|
28
30
|
|
29
31
|
BUNDLED WITH
|
30
|
-
|
32
|
+
2.4.15
|
data/lib/sablon/content.rb
CHANGED
@@ -130,11 +130,16 @@ module Sablon
|
|
130
130
|
# node passed in. Run properties are merged here because of namespace
|
131
131
|
# issues when working with a document fragment
|
132
132
|
def add_siblings_to(node, rpr_tag = nil)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
133
|
+
# Since Nokogiri 1.11.0 adding siblings is only possible for nodes
|
134
|
+
# with a parent because the parent is used as the context node for
|
135
|
+
# parsing markup.
|
136
|
+
if !node.parent.nil?
|
137
|
+
xml.children.reverse.each do |child|
|
138
|
+
node.add_next_sibling child
|
139
|
+
# merge properties
|
140
|
+
next unless rpr_tag
|
141
|
+
merge_rpr_tags(child, rpr_tag.children)
|
142
|
+
end
|
138
143
|
end
|
139
144
|
end
|
140
145
|
|
data/lib/sablon/context.rb
CHANGED
@@ -6,6 +6,9 @@ module Sablon
|
|
6
6
|
# user supplied hash into a data structure suitable for rendering the
|
7
7
|
# docx template.
|
8
8
|
module Context
|
9
|
+
class << self; attr_accessor :content_regex end
|
10
|
+
self.content_regex = /\A([^:]+):(.+)\z/
|
11
|
+
|
9
12
|
class << self
|
10
13
|
def transform_hash(hash)
|
11
14
|
Hash[hash.map { |k, v| transform_pair(k.to_s, v) }]
|
@@ -25,12 +28,12 @@ module Sablon
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def transform_pair(key, value)
|
28
|
-
if
|
31
|
+
if match = content_regex.match(key)
|
29
32
|
if value.nil?
|
30
|
-
[
|
33
|
+
[match[2], value]
|
31
34
|
else
|
32
|
-
|
33
|
-
[
|
35
|
+
type_id = match[1].to_sym
|
36
|
+
[match[2], Content.make(type_id, value)]
|
34
37
|
end
|
35
38
|
else
|
36
39
|
transform_standard_key(key, value)
|
@@ -86,7 +86,7 @@ module Sablon
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
class ImageBlock <
|
89
|
+
class ImageBlock < Block
|
90
90
|
def self.encloses?(start_field, end_field)
|
91
91
|
start_field.expression.start_with?('@')
|
92
92
|
end
|
@@ -100,12 +100,16 @@ module Sablon
|
|
100
100
|
nodes.each do |node|
|
101
101
|
pic_prop = node.at_xpath('.//pic:cNvPr', pic: 'http://schemas.openxmlformats.org/drawingml/2006/picture')
|
102
102
|
pic_prop.attributes['name'].value = image.name if pic_prop
|
103
|
-
blip =
|
103
|
+
blip = if File.extname(image.name) == '.svg'
|
104
|
+
node.at_xpath('.//asvg:svgBlip', asvg: 'http://schemas.microsoft.com/office/drawing/2016/SVG/main')
|
105
|
+
else
|
106
|
+
node.at_xpath('.//a:blip', a: 'http://schemas.openxmlformats.org/drawingml/2006/main')
|
107
|
+
end
|
104
108
|
blip.attributes['embed'].value = image.local_rid if blip
|
105
109
|
drawing_size = node.at_xpath('.//wp:extent')
|
106
110
|
|
107
|
-
|
108
|
-
|
111
|
+
# if image properties are defined, the size of the placeholder
|
112
|
+
# image should be replaced with the actual values
|
109
113
|
if image.width && image.height
|
110
114
|
drawing_size.attributes['cx'].value = image.width.to_s if drawing_size
|
111
115
|
drawing_size.attributes['cy'].value = image.height.to_s if drawing_size
|
@@ -126,6 +130,28 @@ module Sablon
|
|
126
130
|
super && parent(start_field) == parent(end_field)
|
127
131
|
end
|
128
132
|
|
133
|
+
def process(env)
|
134
|
+
# Create a mock document structure so xpath queries will work
|
135
|
+
# correctly on block level content (i.e. searching for the first
|
136
|
+
# ancestor paragraph)
|
137
|
+
doc_node = Nokogiri::XML::Node.new('document', start_node.document)
|
138
|
+
doc_node.namespace = start_node.parent.namespace
|
139
|
+
p_node = Nokogiri::XML::Node.new('p', doc_node.document)
|
140
|
+
p_node.namespace = start_node.parent.namespace
|
141
|
+
p_node.children = Nokogiri::XML::NodeSet.new(p_node.document,
|
142
|
+
body.map(&:dup))
|
143
|
+
doc_node.children = Nokogiri::XML::NodeSet.new(p_node.document,
|
144
|
+
[p_node])
|
145
|
+
Processor::Document.process doc_node, env
|
146
|
+
|
147
|
+
if p_node.parent.nil?
|
148
|
+
replace_parent_node(doc_node.children)
|
149
|
+
[]
|
150
|
+
else
|
151
|
+
p_node.children
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
129
155
|
def remove_control_elements
|
130
156
|
body.each(&:remove)
|
131
157
|
start_field.remove
|
@@ -139,6 +165,16 @@ module Sablon
|
|
139
165
|
def end_node
|
140
166
|
@end_node ||= end_field.start_node
|
141
167
|
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# A block level insertion has occurred which must replace the
|
172
|
+
# parent paragraph of the start node.
|
173
|
+
def replace_parent_node(content)
|
174
|
+
node = start_node.ancestors('.//w:p').first
|
175
|
+
content.each { |n| node.add_next_sibling n }
|
176
|
+
node.remove
|
177
|
+
end
|
142
178
|
end
|
143
179
|
end
|
144
180
|
end
|
data/lib/sablon/template.rb
CHANGED
@@ -53,7 +53,7 @@ module Sablon
|
|
53
53
|
|
54
54
|
def render(context, properties = {})
|
55
55
|
# initialize environment
|
56
|
-
@document = Sablon::DOM::Model.new(Zip::File.open(@path))
|
56
|
+
@document = Sablon::DOM::Model.new(Zip::File.open(@path, !File.exist?(@path)))
|
57
57
|
env = Sablon::Environment.new(self, context)
|
58
58
|
env.section_properties = properties
|
59
59
|
#
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Sablon
|
2
|
+
module Test
|
3
|
+
module Assertions
|
4
|
+
def assert_docx_equal(expected_path, actual_path)
|
5
|
+
#
|
6
|
+
# Parse document archives and generate a diff
|
7
|
+
xml_diffs = diff_docx_files(expected_path, actual_path)
|
8
|
+
#
|
9
|
+
# build error message
|
10
|
+
msg = 'The generated document does not match the sample. Please investigate file(s): '
|
11
|
+
msg += xml_diffs.keys.sort.join(', ')
|
12
|
+
xml_diffs.each do |name, diff_text|
|
13
|
+
msg += "\n#{'-' * 72}\nFile: #{name}\n#{diff_text}\n"
|
14
|
+
end
|
15
|
+
msg += '-' * 72 + "\n"
|
16
|
+
msg += "If the generated document is correct, the sample needs to be updated:\n"
|
17
|
+
msg += "\t cp #{actual_path} #{expected_path}"
|
18
|
+
#
|
19
|
+
raise Minitest::Assertion, msg unless xml_diffs.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Returns a hash of all XML files that differ in the docx file. This
|
24
|
+
# only checks files that have the extension ".xml" or ".rels".
|
25
|
+
def diff_docx_files(expected_path, actual_path)
|
26
|
+
expected = parse_docx(expected_path)
|
27
|
+
actual = parse_docx(actual_path)
|
28
|
+
xml_diffs = {}
|
29
|
+
#
|
30
|
+
expected.each do |entry_name, expect|
|
31
|
+
next unless entry_name =~ /.xml$|.rels$/
|
32
|
+
next unless expect != actual[entry_name]
|
33
|
+
#
|
34
|
+
xml_diffs[entry_name] = diff(expect, actual[entry_name])
|
35
|
+
end
|
36
|
+
#
|
37
|
+
xml_diffs
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def parse_docx(path)
|
43
|
+
contents = {}
|
44
|
+
#
|
45
|
+
# step over all entries adding them to the hash to diff against
|
46
|
+
Zip::File.open(path).each do |entry|
|
47
|
+
next unless entry.file?
|
48
|
+
content = entry.get_input_stream.read
|
49
|
+
# normalize xml content
|
50
|
+
if entry.name =~ /.xml$|.rels$/
|
51
|
+
content = Nokogiri::XML(content).to_xml(indent: 2)
|
52
|
+
end
|
53
|
+
contents[entry.name] = content
|
54
|
+
end
|
55
|
+
#
|
56
|
+
contents
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/sablon/version.rb
CHANGED
data/misc/TEMPLATE.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
## Creating a Simple Template
|
2
|
+
|
2
3
|
Creating a template is as easy as creating a normal Word document (`.docx`). Yet it could be confusing to people who haven't used the [Mail Merge](https://support.office.com/en-us/article/Use-mail-merge-to-send-bulk-email-messages-0f123521-20ce-4aa8-8b62-ac211dedefa4) feature in Microsoft Word. The steps are as follows:
|
3
4
|
|
5
|
+
### Setup
|
4
6
|
- Create a new word document
|
5
7
|
|
6
8
|
![Step 1](/misc/step_1.png)
|
@@ -11,6 +13,22 @@ Creating a template is as easy as creating a normal Word document (`.docx`). Yet
|
|
11
13
|
|
12
14
|
- Add mail merge fields
|
13
15
|
|
16
|
+
See [How to add a mail merge field](#how-to-add-a-mail-merge-field) for specific instructions for your operating system.
|
17
|
+
|
18
|
+
- You should then have something like this:
|
19
|
+
|
20
|
+
![Step 6](/misc/step_6.png)
|
21
|
+
|
22
|
+
- A complete template might look like this:
|
23
|
+
|
24
|
+
![Step 7](/misc/step_7.png)
|
25
|
+
|
26
|
+
NOTE: When adding variables, those that display a value are preceded with an equals sign `=`. Those that just perform logics, such as loops and conditionals, do not need a preceding equals sign.
|
27
|
+
|
28
|
+
### How to add a mail merge field
|
29
|
+
|
30
|
+
#### Microsoft Windows
|
31
|
+
|
14
32
|
- Click the `Insert` tab on the ribbon
|
15
33
|
|
16
34
|
![Step 3.1](/misc/step_3_1.png)
|
@@ -33,12 +51,10 @@ Creating a template is as easy as creating a normal Word document (`.docx`). Yet
|
|
33
51
|
|
34
52
|
![Step 5](/misc/step_5.png)
|
35
53
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
NOTE: When adding variables, those that display a value are preceded with an equals sign `=`. Those that just perform logics, such as loops and conditionals, do not need a preceding equals sign.
|
54
|
+
#### MacOS
|
55
|
+
- From the menu bar, open the **Insert** menu and select **Field...**
|
56
|
+
- Under *Categories*, select **Mail Merge**
|
57
|
+
- Under *Field names*, select **MergeField**
|
58
|
+
- Add your variable name in the input box; make sure to leave a space after the word `MERGEFIELD`
|
59
|
+
- Example: `MERGEFIELD =person.first_name`
|
60
|
+
- Click **OK**
|
data/sablon.gemspec
CHANGED
@@ -22,10 +22,10 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.required_ruby_version = '>= 2.2'
|
23
23
|
|
24
24
|
spec.add_runtime_dependency 'nokogiri', ">= 1.8.5"
|
25
|
-
spec.add_runtime_dependency 'rubyzip', ">= 1.
|
25
|
+
spec.add_runtime_dependency 'rubyzip', ">= 1.3.0"
|
26
26
|
|
27
27
|
spec.add_development_dependency "bundler", ">= 1.6"
|
28
|
-
spec.add_development_dependency "rake", "~>
|
28
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
29
29
|
spec.add_development_dependency "minitest", "~> 5.4"
|
30
30
|
spec.add_development_dependency "xml-simple"
|
31
31
|
end
|
data/test/configuration_test.rb
CHANGED
@@ -75,8 +75,9 @@ class ConfigurationHTMLTagTest < Sablon::TestCase
|
|
75
75
|
|
76
76
|
# Exercising more of the logic used to conform args into valid
|
77
77
|
def test_html_tag_full_init
|
78
|
-
args = ['a', 'inline'
|
79
|
-
|
78
|
+
args = ['a', 'inline']
|
79
|
+
kwargs = { ast_class: Sablon::HTMLConverter::Run }
|
80
|
+
tag = Sablon::Configuration::HTMLTag.new(*args, **kwargs)
|
80
81
|
assert_equal :a, tag.name
|
81
82
|
assert_equal :inline, tag.type
|
82
83
|
assert_equal Sablon::HTMLConverter::Run, tag.ast_class
|
data/test/context_test.rb
CHANGED
@@ -61,4 +61,26 @@ class ContextTest < Sablon::TestCase
|
|
61
61
|
context = Sablon::Context.transform_hash(input_context)
|
62
62
|
assert_equal expected_context, context
|
63
63
|
end
|
64
|
+
|
65
|
+
def test_tune_typed_content_regex
|
66
|
+
input_context = {
|
67
|
+
default: "string",
|
68
|
+
"word_ml:runs" => "<w:r><w:t>Text</w:t><w:r>",
|
69
|
+
"urn:some:example" => "string as well"
|
70
|
+
}
|
71
|
+
expected_context = {
|
72
|
+
"default" => "string",
|
73
|
+
"runs" => Sablon.content(:word_ml, "<w:r><w:t>Text</w:t><w:r>"),
|
74
|
+
"urn:some:example" => "string as well"
|
75
|
+
}
|
76
|
+
|
77
|
+
original_regex = Sablon::Context.content_regex
|
78
|
+
begin
|
79
|
+
Sablon::Context.content_regex = /\A((?!urn:)[^:]+):(.+)\z/
|
80
|
+
context = Sablon::Context.transform_hash(input_context)
|
81
|
+
assert_equal expected_context, context
|
82
|
+
ensure
|
83
|
+
Sablon::Context.content_regex = original_regex
|
84
|
+
end
|
85
|
+
end
|
64
86
|
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -39,7 +39,7 @@ class HTMLConverterASTBuilderTest < Sablon::TestCase
|
|
39
39
|
def test_merge_properties
|
40
40
|
@builder = new_builder
|
41
41
|
node = Nokogiri::HTML.fragment('<span style="color: #F00; text-decoration: underline wavy">Test</span>').children[0]
|
42
|
-
tag = Struct.new(:properties).new(rStyle: 'Normal')
|
42
|
+
tag = Struct.new(:properties).new({ rStyle: 'Normal' })
|
43
43
|
# test that properties are merged across all three arguments
|
44
44
|
props = @builder.send(:merge_node_properties, node, tag, 'background-color' => '#00F')
|
45
45
|
assert_equal({ 'background-color' => '#00F', rStyle: 'Normal', 'color' => '#F00', 'text-decoration' => 'underline wavy' }, props)
|
data/test/sablon_test.rb
CHANGED
@@ -105,7 +105,8 @@ class SablonConditionalsTest < Sablon::TestCase
|
|
105
105
|
object: OpenStruct.new(true_method: true, false_method: false),
|
106
106
|
success_content: '✓',
|
107
107
|
fail_content: '✗',
|
108
|
-
content: 'Some Content'
|
108
|
+
content: 'Some Content',
|
109
|
+
block_content: Sablon.content(:html, '<p>HTML paragraph injected</p>')
|
109
110
|
}
|
110
111
|
#
|
111
112
|
template.render_to_file @output_path, context
|
@@ -155,7 +156,7 @@ class SablonImagesTest < Sablon::TestCase
|
|
155
156
|
darth = Sablon.content(:image, @image_fixtures.join('darth_vader.jpg'))
|
156
157
|
#
|
157
158
|
im_data = StringIO.new(IO.binread(@image_fixtures.join('clone.jpg')))
|
158
|
-
trooper = Sablon.content(:image, im_data, filename: 'clone.jpg', properties: {height: '
|
159
|
+
trooper = Sablon.content(:image, im_data, filename: 'clone.jpg', properties: {height: '1cm', width: '4cm'})
|
159
160
|
#
|
160
161
|
# with the following context setup all trooper should be reused and
|
161
162
|
# only a single file added to media. R2D2 should get duplicated in the
|
@@ -186,3 +187,25 @@ class SablonImagesTest < Sablon::TestCase
|
|
186
187
|
assert_equal "Filename: 'clone' has no discernable extension", e.message
|
187
188
|
end
|
188
189
|
end
|
190
|
+
|
191
|
+
class SablonSvgImagesTest < Sablon::TestCase
|
192
|
+
def setup
|
193
|
+
super
|
194
|
+
@base_path = Pathname.new(File.expand_path("../", __FILE__))
|
195
|
+
@template_path = @base_path + "fixtures/svg_images_template.docx"
|
196
|
+
@output_path = @base_path + "sandbox/svg_images.docx"
|
197
|
+
@sample_path = @base_path + "fixtures/svg_images_sample.docx"
|
198
|
+
@image_fixtures = @base_path + "fixtures/images"
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_generate_document_from_template
|
202
|
+
template = Sablon.template @template_path
|
203
|
+
|
204
|
+
context = {
|
205
|
+
svg_sample: Sablon.content(:image, @image_fixtures.join('svg_sample.svg'))
|
206
|
+
}
|
207
|
+
|
208
|
+
template.render_to_file @output_path, context
|
209
|
+
assert_docx_equal @sample_path, @output_path
|
210
|
+
end
|
211
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -8,64 +8,11 @@ require "pathname"
|
|
8
8
|
|
9
9
|
$: << File.expand_path('../../lib', __FILE__)
|
10
10
|
require "sablon"
|
11
|
+
require "sablon/test/assertions"
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
def assert_docx_equal(expected_path, actual_path)
|
15
|
-
#
|
16
|
-
# Parse document archives and generate a diff
|
17
|
-
xml_diffs = diff_docx_files(expected_path, actual_path)
|
18
|
-
#
|
19
|
-
# build error message
|
20
|
-
msg = 'The generated document does not match the sample. Please investigate file(s): '
|
21
|
-
msg += xml_diffs.keys.sort.join(', ')
|
22
|
-
xml_diffs.each do |name, diff_text|
|
23
|
-
msg += "\n#{'-' * 72}\nFile: #{name}\n#{diff_text}\n"
|
24
|
-
end
|
25
|
-
msg += '-' * 72 + "\n"
|
26
|
-
msg += "If the generated document is correct, the sample needs to be updated:\n"
|
27
|
-
msg += "\t cp #{actual_path} #{expected_path}"
|
28
|
-
#
|
29
|
-
raise Minitest::Assertion, msg unless xml_diffs.empty?
|
30
|
-
end
|
31
|
-
|
32
|
-
# Returns a hash of all XML files that differ in the docx file. This
|
33
|
-
# only checks files that have the extension ".xml" or ".rels".
|
34
|
-
def diff_docx_files(expected_path, actual_path)
|
35
|
-
expected = parse_docx(expected_path)
|
36
|
-
actual = parse_docx(actual_path)
|
37
|
-
xml_diffs = {}
|
38
|
-
#
|
39
|
-
expected.each do |entry_name, expect|
|
40
|
-
next unless entry_name =~ /.xml$|.rels$/
|
41
|
-
next unless expect != actual[entry_name]
|
42
|
-
#
|
43
|
-
xml_diffs[entry_name] = diff(expect, actual[entry_name])
|
44
|
-
end
|
45
|
-
#
|
46
|
-
xml_diffs
|
47
|
-
end
|
48
|
-
|
49
|
-
def parse_docx(path)
|
50
|
-
contents = {}
|
51
|
-
#
|
52
|
-
# step over all entries adding them to the hash to diff against
|
53
|
-
Zip::File.open(path).each do |entry|
|
54
|
-
next unless entry.file?
|
55
|
-
content = entry.get_input_stream.read
|
56
|
-
# normalize xml content
|
57
|
-
if entry.name =~ /.xml$|.rels$/
|
58
|
-
content = Nokogiri::XML(content).to_xml(indent: 2)
|
59
|
-
end
|
60
|
-
contents[entry.name] = content
|
61
|
-
end
|
62
|
-
#
|
63
|
-
contents
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
13
|
+
class Sablon::TestCase < Minitest::Test
|
14
|
+
include Sablon::Test::Assertions
|
67
15
|
|
68
|
-
class Sablon::TestCase < MiniTest::Test
|
69
16
|
def teardown
|
70
17
|
super
|
71
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sablon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yves Senn
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.
|
33
|
+
version: 1.3.0
|
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
|
-
version: 1.
|
40
|
+
version: 1.3.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '13.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '13.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: minitest
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,6 +137,7 @@ files:
|
|
137
137
|
- lib/sablon/processor/section_properties.rb
|
138
138
|
- lib/sablon/template.rb
|
139
139
|
- lib/sablon/test.rb
|
140
|
+
- lib/sablon/test/assertions.rb
|
140
141
|
- lib/sablon/version.rb
|
141
142
|
- misc/TEMPLATE.md
|
142
143
|
- misc/cv_sample.png
|
@@ -174,6 +175,7 @@ files:
|
|
174
175
|
- test/fixtures/images/clone.jpg
|
175
176
|
- test/fixtures/images/darth_vader.jpg
|
176
177
|
- test/fixtures/images/r2d2.jpg
|
178
|
+
- test/fixtures/images/svg_sample.svg
|
177
179
|
- test/fixtures/images_sample.docx
|
178
180
|
- test/fixtures/images_template.docx
|
179
181
|
- test/fixtures/insertion_template.docx
|
@@ -184,6 +186,8 @@ files:
|
|
184
186
|
- test/fixtures/recipe_context.json
|
185
187
|
- test/fixtures/recipe_sample.docx
|
186
188
|
- test/fixtures/recipe_template.docx
|
189
|
+
- test/fixtures/svg_images_sample.docx
|
190
|
+
- test/fixtures/svg_images_template.docx
|
187
191
|
- test/fixtures/xml/comment.xml
|
188
192
|
- test/fixtures/xml/comment_block_and_comment_as_key.xml
|
189
193
|
- test/fixtures/xml/complex_field.xml
|
@@ -227,7 +231,7 @@ homepage: http://github.com/senny/sablon
|
|
227
231
|
licenses:
|
228
232
|
- MIT
|
229
233
|
metadata: {}
|
230
|
-
post_install_message:
|
234
|
+
post_install_message:
|
231
235
|
rdoc_options: []
|
232
236
|
require_paths:
|
233
237
|
- lib
|
@@ -242,9 +246,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
246
|
- !ruby/object:Gem::Version
|
243
247
|
version: '0'
|
244
248
|
requirements: []
|
245
|
-
|
246
|
-
|
247
|
-
signing_key:
|
249
|
+
rubygems_version: 3.5.9
|
250
|
+
signing_key:
|
248
251
|
specification_version: 4
|
249
252
|
summary: docx template processor
|
250
253
|
test_files:
|
@@ -267,6 +270,7 @@ test_files:
|
|
267
270
|
- test/fixtures/images/clone.jpg
|
268
271
|
- test/fixtures/images/darth_vader.jpg
|
269
272
|
- test/fixtures/images/r2d2.jpg
|
273
|
+
- test/fixtures/images/svg_sample.svg
|
270
274
|
- test/fixtures/images_sample.docx
|
271
275
|
- test/fixtures/images_template.docx
|
272
276
|
- test/fixtures/insertion_template.docx
|
@@ -277,6 +281,8 @@ test_files:
|
|
277
281
|
- test/fixtures/recipe_context.json
|
278
282
|
- test/fixtures/recipe_sample.docx
|
279
283
|
- test/fixtures/recipe_template.docx
|
284
|
+
- test/fixtures/svg_images_sample.docx
|
285
|
+
- test/fixtures/svg_images_template.docx
|
280
286
|
- test/fixtures/xml/comment.xml
|
281
287
|
- test/fixtures/xml/comment_block_and_comment_as_key.xml
|
282
288
|
- test/fixtures/xml/complex_field.xml
|