sablon 0.0.19.beta3 → 0.0.19.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/sablon/html/ast.rb +59 -20
- data/lib/sablon/html/converter.rb +8 -8
- data/lib/sablon/operations.rb +5 -1
- data/lib/sablon/version.rb +1 -1
- data/test/fixtures/html_sample.docx +0 -0
- data/test/html/converter_test.rb +49 -16
- data/test/html_test.rb +1 -1
- data/test/processor/document_test.rb +12 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac235d2921b38ea77aa5aef274e73aa34e84ffcf
|
4
|
+
data.tar.gz: 9ce6a08012f8540e2a1998d35a177a9e6b95a909
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 219cb0acb4fdbf3eedc347577dc4c42996e78eb792a51394261e60a48d7b8114cd6d3668d91bcd00f0bdeb6406d0e0f3a2fb4ff63f12e12454bb5682cdbe967d
|
7
|
+
data.tar.gz: 51128d4324f508395837d57cf8330dabed48729ebe8518bacafd18416fa502b0e2a8dfbf4e980ab763a93f5f49c50f5330739dde185780d8f02ac83f66776b59
|
data/Gemfile.lock
CHANGED
data/lib/sablon/html/ast.rb
CHANGED
@@ -26,18 +26,22 @@ module Sablon
|
|
26
26
|
def to_docx
|
27
27
|
nodes.map(&:to_docx).join
|
28
28
|
end
|
29
|
-
end
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
nodes
|
30
|
+
def inspect
|
31
|
+
"[#{nodes.map(&:inspect).join(', ')}]"
|
34
32
|
end
|
33
|
+
end
|
35
34
|
|
35
|
+
class Root < Collection
|
36
36
|
def grep(pattern)
|
37
37
|
visitor = GrepVisitor.new(pattern)
|
38
38
|
accept(visitor)
|
39
39
|
visitor.result
|
40
40
|
end
|
41
|
+
|
42
|
+
def inspect
|
43
|
+
"<Root: #{super}>"
|
44
|
+
end
|
41
45
|
end
|
42
46
|
|
43
47
|
class Paragraph < Node
|
@@ -65,6 +69,10 @@ XML
|
|
65
69
|
runs.accept(visitor)
|
66
70
|
end
|
67
71
|
|
72
|
+
def inspect
|
73
|
+
"<Paragraph{#{style}}: #{runs.inspect}>"
|
74
|
+
end
|
75
|
+
|
68
76
|
private
|
69
77
|
def ppr_docx
|
70
78
|
end
|
@@ -90,34 +98,61 @@ XML
|
|
90
98
|
end
|
91
99
|
end
|
92
100
|
|
93
|
-
class
|
94
|
-
|
95
|
-
|
96
|
-
@
|
101
|
+
class TextFormat
|
102
|
+
def initialize(bold, italic)
|
103
|
+
@bold = bold
|
104
|
+
@italic = italic
|
105
|
+
end
|
106
|
+
|
107
|
+
def inspect
|
108
|
+
parts = []
|
109
|
+
parts << 'bold' if @bold
|
110
|
+
parts << 'italic' if @italic
|
111
|
+
parts.join('|')
|
97
112
|
end
|
98
113
|
|
99
114
|
def to_docx
|
100
|
-
|
115
|
+
styles = []
|
116
|
+
styles << '<w:b />' if @bold
|
117
|
+
styles << '<w:i />' if @italic
|
118
|
+
if styles.any?
|
119
|
+
"<w:rPr>#{styles.join}</w:rPr>"
|
120
|
+
else
|
121
|
+
''
|
122
|
+
end
|
101
123
|
end
|
102
124
|
|
103
|
-
|
104
|
-
|
125
|
+
def self.default
|
126
|
+
@default ||= new(false, false)
|
105
127
|
end
|
106
128
|
|
107
|
-
def
|
108
|
-
|
129
|
+
def with_bold
|
130
|
+
TextFormat.new(true, @italic)
|
109
131
|
end
|
110
|
-
end
|
111
132
|
|
112
|
-
|
113
|
-
|
114
|
-
'<w:rPr><w:b /></w:rPr>'
|
133
|
+
def with_italic
|
134
|
+
TextFormat.new(@bold, true)
|
115
135
|
end
|
116
136
|
end
|
117
137
|
|
118
|
-
class
|
119
|
-
|
120
|
-
|
138
|
+
class Text < Node
|
139
|
+
attr_reader :string
|
140
|
+
def initialize(string, format)
|
141
|
+
@string = string
|
142
|
+
@format = format
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_docx
|
146
|
+
"<w:r>#{@format.to_docx}<w:t xml:space=\"preserve\">#{normalized_string}</w:t></w:r>"
|
147
|
+
end
|
148
|
+
|
149
|
+
def inspect
|
150
|
+
"<Text{#{@format.inspect}}: #{string}>"
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
def normalized_string
|
155
|
+
string.tr("\u00A0", ' ')
|
121
156
|
end
|
122
157
|
end
|
123
158
|
|
@@ -125,6 +160,10 @@ XML
|
|
125
160
|
def to_docx
|
126
161
|
"<w:r><w:br/></w:r>"
|
127
162
|
end
|
163
|
+
|
164
|
+
def inspect
|
165
|
+
"<Newline>"
|
166
|
+
end
|
128
167
|
end
|
129
168
|
end
|
130
169
|
end
|
@@ -88,10 +88,10 @@ module Sablon
|
|
88
88
|
node = @builder.next
|
89
89
|
if node.name == 'div'
|
90
90
|
@builder.new_layer
|
91
|
-
@builder.emit Paragraph.new('Normal',
|
91
|
+
@builder.emit Paragraph.new('Normal', ast_text(node.children))
|
92
92
|
elsif node.name == 'p'
|
93
93
|
@builder.new_layer
|
94
|
-
@builder.emit Paragraph.new('Paragraph',
|
94
|
+
@builder.emit Paragraph.new('Paragraph', ast_text(node.children))
|
95
95
|
elsif node.name == 'ul'
|
96
96
|
@builder.new_layer ilvl: true
|
97
97
|
unless @builder.nested?
|
@@ -106,7 +106,7 @@ module Sablon
|
|
106
106
|
@builder.push_all(node.children)
|
107
107
|
elsif node.name == 'li'
|
108
108
|
@builder.new_layer
|
109
|
-
@builder.emit ListParagraph.new(@definition.style,
|
109
|
+
@builder.emit ListParagraph.new(@definition.style, ast_text(node.children), @definition.numid, @builder.ilvl)
|
110
110
|
elsif node.text?
|
111
111
|
# SKIP?
|
112
112
|
else
|
@@ -114,16 +114,16 @@ module Sablon
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
def
|
118
|
-
runs = nodes.
|
117
|
+
def ast_text(nodes, format: TextFormat.default)
|
118
|
+
runs = nodes.flat_map do |node|
|
119
119
|
if node.text?
|
120
|
-
Text.new(node.text)
|
120
|
+
Text.new(node.text, format)
|
121
121
|
elsif node.name == 'br'
|
122
122
|
Newline.new
|
123
123
|
elsif node.name == 'strong'
|
124
|
-
|
124
|
+
ast_text(node.children, format: format.with_bold).nodes
|
125
125
|
elsif node.name == 'em'
|
126
|
-
|
126
|
+
ast_text(node.children, format: format.with_italic).nodes
|
127
127
|
elsif ['ul', 'ol', 'p', 'div'].include?(node.name)
|
128
128
|
@builder.push(node)
|
129
129
|
nil
|
data/lib/sablon/operations.rb
CHANGED
@@ -3,7 +3,11 @@ module Sablon
|
|
3
3
|
module Statement
|
4
4
|
class Insertion < Struct.new(:expr, :field)
|
5
5
|
def evaluate(context)
|
6
|
-
|
6
|
+
if content = expr.evaluate(context)
|
7
|
+
field.replace(Sablon::Content.wrap(expr.evaluate(context)))
|
8
|
+
else
|
9
|
+
field.remove
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
9
13
|
|
data/lib/sablon/version.rb
CHANGED
Binary file
|
data/test/html/converter_test.rb
CHANGED
@@ -83,6 +83,25 @@ DOCX
|
|
83
83
|
assert_equal normalize_wordml(expected_output), @converter.process(input)
|
84
84
|
end
|
85
85
|
|
86
|
+
def test_convert_br_tags_inside_strong
|
87
|
+
input = '<div><strong><br />Lorem ipsum<br />dolor sit amet</strong></div>'
|
88
|
+
expected_output = <<-DOCX
|
89
|
+
<w:p>
|
90
|
+
<w:pPr><w:pStyle w:val="Normal" /></w:pPr>
|
91
|
+
<w:r><w:br/></w:r>
|
92
|
+
<w:r>
|
93
|
+
<w:rPr><w:b /></w:rPr>
|
94
|
+
<w:t xml:space="preserve">Lorem ipsum</w:t></w:r>
|
95
|
+
<w:r><w:br/></w:r>
|
96
|
+
<w:r>
|
97
|
+
<w:rPr><w:b /></w:rPr>
|
98
|
+
<w:t xml:space="preserve">dolor sit amet</w:t>
|
99
|
+
</w:r>
|
100
|
+
</w:p>
|
101
|
+
DOCX
|
102
|
+
assert_equal normalize_wordml(expected_output), @converter.process(input)
|
103
|
+
end
|
104
|
+
|
86
105
|
def test_unorderd_lists
|
87
106
|
input = '<ul><li>Lorem</li><li>ipsum</li><li>dolor</li></ul>'
|
88
107
|
expected_output = <<-DOCX.strip
|
@@ -263,42 +282,56 @@ class HTMLConverterASTTest < Sablon::TestCase
|
|
263
282
|
|
264
283
|
def test_div
|
265
284
|
input = '<div>Lorem ipsum dolor sit amet</div>'
|
266
|
-
ast = @converter.processed_ast(input)
|
267
|
-
assert_equal [
|
268
|
-
assert_equal ['Normal'], ast.map(&:style)
|
285
|
+
ast = @converter.processed_ast(input)
|
286
|
+
assert_equal '<Root: [<Paragraph{Normal}: [<Text{}: Lorem ipsum dolor sit amet>]>]>', ast.inspect
|
269
287
|
end
|
270
288
|
|
271
289
|
def test_p
|
272
290
|
input = '<p>Lorem ipsum dolor sit amet</p>'
|
273
|
-
ast = @converter.processed_ast(input)
|
274
|
-
assert_equal [
|
275
|
-
|
291
|
+
ast = @converter.processed_ast(input)
|
292
|
+
assert_equal '<Root: [<Paragraph{Paragraph}: [<Text{}: Lorem ipsum dolor sit amet>]>]>', ast.inspect
|
293
|
+
end
|
294
|
+
|
295
|
+
def test_br_in_strong
|
296
|
+
input = '<div><strong>Lorem<br />ipsum<br />dolor</strong></div>'
|
297
|
+
par = @converter.processed_ast(input).grep(Sablon::HTMLConverter::Paragraph).first
|
298
|
+
assert_equal "[<Text{bold}: Lorem>, <Newline>, <Text{bold}: ipsum>, <Newline>, <Text{bold}: dolor>]", par.runs.inspect
|
299
|
+
end
|
300
|
+
|
301
|
+
def test_br_in_em
|
302
|
+
input = '<div><em>Lorem<br />ipsum<br />dolor</em></div>'
|
303
|
+
par = @converter.processed_ast(input).grep(Sablon::HTMLConverter::Paragraph).first
|
304
|
+
assert_equal "[<Text{italic}: Lorem>, <Newline>, <Text{italic}: ipsum>, <Newline>, <Text{italic}: dolor>]", par.runs.inspect
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_nested_strong_and_em
|
308
|
+
input = '<div><strong>Lorem <em>ipsum</em> dolor</strong></div>'
|
309
|
+
par = @converter.processed_ast(input).grep(Sablon::HTMLConverter::Paragraph).first
|
310
|
+
assert_equal "[<Text{bold}: Lorem >, <Text{bold|italic}: ipsum>, <Text{bold}: dolor>]", par.runs.inspect
|
276
311
|
end
|
277
312
|
|
278
313
|
def test_ignore_last_br_in_div
|
279
314
|
input = '<div>Lorem ipsum dolor sit amet<br /></div>'
|
280
|
-
par = @converter.processed_ast(input).
|
281
|
-
assert_equal [
|
315
|
+
par = @converter.processed_ast(input).grep(Sablon::HTMLConverter::Paragraph).first
|
316
|
+
assert_equal "[<Text{}: Lorem ipsum dolor sit amet>]", par.runs.inspect
|
282
317
|
end
|
283
318
|
|
284
319
|
def test_ignore_br_in_blank_div
|
285
320
|
input = '<div><br /></div>'
|
286
|
-
par = @converter.processed_ast(input).
|
287
|
-
assert_equal [], par.runs.
|
321
|
+
par = @converter.processed_ast(input).grep(Sablon::HTMLConverter::Paragraph).first
|
322
|
+
assert_equal "[]", par.runs.inspect
|
288
323
|
end
|
289
324
|
|
290
325
|
def test_ul
|
291
326
|
input = '<ul><li>Lorem</li><li>ipsum</li></ul>'
|
292
|
-
ast = @converter.processed_ast(input)
|
293
|
-
assert_equal [
|
294
|
-
assert_equal ["ListBullet", "ListBullet"], ast.map(&:style)
|
327
|
+
ast = @converter.processed_ast(input)
|
328
|
+
assert_equal "<Root: [<Paragraph{ListBullet}: [<Text{}: Lorem>]>, <Paragraph{ListBullet}: [<Text{}: ipsum>]>]>", ast.inspect
|
295
329
|
end
|
296
330
|
|
297
331
|
def test_ol
|
298
332
|
input = '<ol><li>Lorem</li><li>ipsum</li></ol>'
|
299
|
-
ast = @converter.processed_ast(input)
|
300
|
-
assert_equal [
|
301
|
-
assert_equal ["ListNumber", "ListNumber"], ast.map(&:style)
|
333
|
+
ast = @converter.processed_ast(input)
|
334
|
+
assert_equal "<Root: [<Paragraph{ListNumber}: [<Text{}: Lorem>]>, <Paragraph{ListNumber}: [<Text{}: ipsum>]>]>", ast.inspect
|
302
335
|
end
|
303
336
|
|
304
337
|
def test_num_id
|
data/test/html_test.rb
CHANGED
@@ -39,7 +39,7 @@ class SablonHTMLTest < Sablon::TestCase
|
|
39
39
|
private
|
40
40
|
def content
|
41
41
|
<<-HTML
|
42
|
-
<div>Lorem <strong>ipsum</strong> <em>dolor</em> <strong>sit</strong> <em>amet</em>, <strong>consectetur adipiscing elit</strong>. <em>Suspendisse a tempus turpis</em>. Duis urna justo, vehicula vitae ultricies vel, congue at sem. Fusce turpis turpis, aliquet id pulvinar aliquam, iaculis non elit. Nulla feugiat lectus nulla, in dictum ipsum cursus ac. Quisque at odio neque. Sed ac tortor iaculis, bibendum leo ut, malesuada velit. Donec iaculis sed urna eget pharetra. Praesent ornare fermentum turpis, placerat iaculis urna bibendum vitae. Nunc in quam consequat, tristique tellus in, commodo turpis. Curabitur ullamcorper odio purus, lobortis egestas magna laoreet vitae. Nunc fringilla velit ante, eu aliquam nisi cursus vitae. Suspendisse sit amet dui egestas, volutpat nisi vel, mattis justo. Nullam pellentesque, ipsum eget blandit pharetra, augue elit aliquam mauris, vel mollis nisl augue ut ipsum.</div><ol><li>Vestibulum <ol><li>ante ipsum primis </li></ol></li><li>in faucibus orci luctus <ol><li>et ultrices posuere cubilia Curae; <ol><li>Aliquam vel dolor </li><li>sed sem maximus </li></ol></li><li>fermentum in non odio. <ol><li>Fusce hendrerit ornare mollis. </li></ol></li><li>Nunc scelerisque nibh nec turpis tempor pulvinar. </li></ol></li><li>Donec eros turpis, </li><li>aliquet vel volutpat sit amet, <ol><li>semper eu purus. </li><li>Proin ac erat nec urna efficitur vulputate. <ol><li>Quisque varius convallis ultricies. </li><li>Nullam vel fermentum eros. </li></ol></li></ol></li></ol><div>Pellentesque nulla leo, auctor ornare erat sed, rhoncus congue diam. Duis non porttitor nulla, ut eleifend enim. Pellentesque non tempor sem.</div><div>Mauris auctor egestas arcu, </div><ol><li>id venenatis nibh dignissim id. </li><li>In non placerat metus. </li></ol><ul><li>Nunc sed consequat metus. </li><li>Nulla consectetur lorem consequat, </li><li>malesuada dui at, lacinia lectus. </li></ul><ol><li>Aliquam efficitur </li><li>lorem a mauris feugiat, </li><li>at semper eros pellentesque. </li></ol><div>Nunc lacus diam, consectetur ut odio sit amet, placerat pharetra erat. Sed commodo ut sem id congue. Sed eget neque elit. Curabitur at erat tortor. Maecenas eget sapien vitae est sagittis accumsan et nec orci. Integer luctus at nisl eget venenatis. Nunc nunc eros, consectetur at tortor et, tristique ultrices elit. Nulla in turpis nibh.</div><ul><li>Nam consectetur <ul><li>venenatis tempor. </li></ul></li><li>Aenean <ul><li>blandit<ul><li>porttitor massa, <ul><li>non efficitur <ul><li>metus. </li></ul></li></ul></li></ul></li></ul></li><li>Duis faucibus nunc nec venenatis faucibus. </li><li>Aliquam erat volutpat. </li></ul><div><strong>Quisque non neque ut lacus eleifend volutpat quis sed lacus
|
42
|
+
<div>Lorem <strong>ipsum</strong> <em>dolor</em> <strong>sit</strong> <em>amet</em>, <strong>consectetur adipiscing elit</strong>. <em>Suspendisse a tempus turpis</em>. Duis urna justo, vehicula vitae ultricies vel, congue at sem. Fusce turpis turpis, aliquet id pulvinar aliquam, iaculis non elit. Nulla feugiat lectus nulla, in dictum ipsum cursus ac. Quisque at odio neque. Sed ac tortor iaculis, bibendum leo ut, malesuada velit. Donec iaculis sed urna eget pharetra. Praesent ornare fermentum turpis, placerat iaculis urna bibendum vitae. Nunc in quam consequat, tristique tellus in, commodo turpis. Curabitur ullamcorper odio purus, lobortis egestas magna laoreet vitae. Nunc fringilla velit ante, eu aliquam nisi cursus vitae. Suspendisse sit amet dui egestas, volutpat nisi vel, mattis justo. Nullam pellentesque, ipsum eget blandit pharetra, augue elit aliquam mauris, vel mollis nisl augue ut ipsum.</div><ol><li>Vestibulum <ol><li>ante ipsum primis </li></ol></li><li>in faucibus orci luctus <ol><li>et ultrices posuere cubilia Curae; <ol><li>Aliquam vel dolor </li><li>sed sem maximus </li></ol></li><li>fermentum in non odio. <ol><li>Fusce hendrerit ornare mollis. </li></ol></li><li>Nunc scelerisque nibh nec turpis tempor pulvinar. </li></ol></li><li>Donec eros turpis, </li><li>aliquet vel volutpat sit amet, <ol><li>semper eu purus. </li><li>Proin ac erat nec urna efficitur vulputate. <ol><li>Quisque varius convallis ultricies. </li><li>Nullam vel fermentum eros. </li></ol></li></ol></li></ol><div>Pellentesque nulla leo, auctor ornare erat sed, rhoncus congue diam. Duis non porttitor nulla, ut eleifend enim. Pellentesque non tempor sem.</div><div>Mauris auctor egestas arcu, </div><ol><li>id venenatis nibh dignissim id. </li><li>In non placerat metus. </li></ol><ul><li>Nunc sed consequat metus. </li><li>Nulla consectetur lorem consequat, </li><li>malesuada dui at, lacinia lectus. </li></ul><ol><li>Aliquam efficitur </li><li>lorem a mauris feugiat, </li><li>at semper eros pellentesque. </li></ol><div>Nunc lacus diam, consectetur ut odio sit amet, placerat pharetra erat. Sed commodo ut sem id congue. Sed eget neque elit. Curabitur at erat tortor. Maecenas eget sapien vitae est sagittis accumsan et nec orci. Integer luctus at nisl eget venenatis. Nunc nunc eros, consectetur at tortor et, tristique ultrices elit. Nulla in turpis nibh.</div><ul><li>Nam consectetur <ul><li>venenatis tempor. </li></ul></li><li>Aenean <ul><li>blandit<ul><li>porttitor massa, <ul><li>non efficitur <ul><li>metus. </li></ul></li></ul></li></ul></li></ul></li><li>Duis faucibus nunc nec venenatis faucibus. </li><li>Aliquam erat volutpat. </li></ul><div><strong>Quisque non neque ut lacus eleifend volutpat quis sed lacus.<br />Praesent ultrices purus eu quam elementum, sit amet faucibus elit interdum. In lectus orci,<br /> elementum quis dictum ac, porta ac ante. Fusce tempus ac mauris id cursus. Phasellus a erat nulla. <em>Mauris dolor orci</em>, malesuada auctor dignissim non, posuere nec odio. Etiam hendrerit justo nec diam ullamcorper, nec blandit elit sodales.</strong></div>
|
43
43
|
HTML
|
44
44
|
end
|
45
45
|
end
|
@@ -28,6 +28,18 @@ class ProcessorDocumentTest < Sablon::TestCase
|
|
28
28
|
document
|
29
29
|
end
|
30
30
|
|
31
|
+
def test_simple_field_replacement_with_nil
|
32
|
+
result = process(snippet("simple_field"), {"first_name" => nil})
|
33
|
+
|
34
|
+
assert_equal "Hello! My Name is , nice to meet you.", text(result)
|
35
|
+
assert_xml_equal <<-document, result
|
36
|
+
<w:p>
|
37
|
+
<w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
|
38
|
+
<w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
|
39
|
+
</w:p>
|
40
|
+
document
|
41
|
+
end
|
42
|
+
|
31
43
|
def test_context_can_contain_string_and_symbol_keys
|
32
44
|
result = process(snippet("simple_fields"), {"first_name" => "Jack", last_name: "Davis"})
|
33
45
|
assert_equal "Jack Davis", text(result)
|
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.0.19.
|
4
|
+
version: 0.0.19.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yves Senn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|