lm_docstache 2.1.2 → 3.0.4
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/CHANGELOG.md +32 -0
- data/lib/lm_docstache/document.rb +4 -0
- data/lib/lm_docstache/hide_custom_tags.rb +30 -17
- data/lib/lm_docstache/parser.rb +63 -15
- data/lib/lm_docstache/renderer.rb +2 -1
- data/lib/lm_docstache/version.rb +1 -1
- data/spec/example_input/sample-signature-with-tabs-spacing.docx +0 -0
- data/spec/hide_custom_tags_spec.rb +43 -17
- data/spec/integration_spec.rb +26 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce8fea2c12829636bd22622e1c022cf2ead4ec09997b7e13322f2b94b4261654
|
4
|
+
data.tar.gz: a25d02153cb1a53bf74111dc59710d83a133a821c4632607c60e06531b28b4ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99bcdac9eea8b1d62e0733b692c600be5e8dcb88710f58d22299b6daa7eabab5f5b01eeb83e61668709655869f6580e918ef38d11bb5ae781db4462b38abba65
|
7
|
+
data.tar.gz: ea96603b65d984edfeeb9aa9f9b760c9665af2c5df163e4c3dae076b24ebc10fccb8e1bdee1cc6e01761496a4b89b7454e80965d6a24e96eb67b53b17e46be84
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 3.0.4
|
4
|
+
* Allow replacement `data` argument to be an `Array`. This feature allow to replace blocks
|
5
|
+
in a sequentially order following the sequence of matching blocks order.
|
6
|
+
|
7
|
+
## 3.0.3
|
8
|
+
|
9
|
+
### Bugfix
|
10
|
+
|
11
|
+
* Hide custom tags arguments was pushing blocks tags to end of paragraph. There are cases this approach
|
12
|
+
doesn't work. I changed to be an ordered replacement when we match hide tags.
|
13
|
+
* Avoid to merge Tab tags on fix errors methods. This was causing unexpected document changes.
|
14
|
+
|
15
|
+
## 3.0.2
|
16
|
+
|
17
|
+
### Bugfix
|
18
|
+
|
19
|
+
* Fix replacing tags related to hidden custom tags regexp formats. E.g. tab characters.
|
20
|
+
|
21
|
+
## 3.0.1
|
22
|
+
|
23
|
+
### Bugfix
|
24
|
+
|
25
|
+
* Fix Hide Custom Tag feature when document there is no text inside a w|r we
|
26
|
+
can't split content.
|
27
|
+
|
28
|
+
## 3.0.0
|
29
|
+
|
30
|
+
## Breaking Changes
|
31
|
+
* Replaced Renderer `hide_custom_tags` options to be a `Hash` instead of `Array`.
|
32
|
+
There are are edge cases which we want to replace the content on hide custom tags.
|
33
|
+
All documentations can be followed on `Renderer` and `HideCustomTag` classes.
|
34
|
+
|
3
35
|
## 2.1.2
|
4
36
|
|
5
37
|
### Bugfix
|
@@ -138,6 +138,10 @@ module LMDocstache
|
|
138
138
|
previous_text_node = previous_run_node.at_css('w|t')
|
139
139
|
current_text_node = run_node.at_css('w|t')
|
140
140
|
|
141
|
+
# avoid to merge blocks with tabs
|
142
|
+
next if run_node.at_css('w|tab')
|
143
|
+
next if previous_run_node.at_css('w|tab')
|
144
|
+
|
141
145
|
next if style_html != previous_style_html
|
142
146
|
next if current_text_node.nil? || previous_text_node.nil?
|
143
147
|
|
@@ -2,43 +2,56 @@ module LMDocstache
|
|
2
2
|
class HideCustomTags
|
3
3
|
attr_reader :document, :hide_custom_tags
|
4
4
|
|
5
|
-
# The +hide_custom_tags+ options is
|
6
|
-
# the pattern you expect to keep at the document but
|
5
|
+
# The +hide_custom_tags+ options is a +Hash+ of +Regexp+ or +String+ keys representing
|
6
|
+
# the pattern you expect to keep at the document but replacing the content to use
|
7
|
+
# font color equal to document background color or white.
|
8
|
+
# For the +Hash+ values we can have:
|
7
9
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
10
|
+
# * +false+ -> In this case we don't change the text content.
|
11
|
+
# * +Proc+ -> When a +Proc+ instance is provided, it's expected it to be
|
12
|
+
# able to receive the matched string and to return the string that will be
|
13
|
+
# used as replacement.
|
14
|
+
# * any other value that will be turned into a string -> in this case, this
|
15
|
+
# will be the value that will replace the matched string
|
16
|
+
def initialize(document:, hide_custom_tags: {})
|
12
17
|
@document = document
|
13
18
|
@hide_custom_tags = hide_custom_tags
|
14
19
|
end
|
15
20
|
|
16
21
|
# Find all run nodes matching hide custom tags +Regexp's+ options you defined, split it
|
17
22
|
# in multiple runs and replace font color to document background color or white in the matching tag run node.
|
23
|
+
# Replace content if you have defined any replacement value.
|
18
24
|
def hide_custom_tags!
|
19
|
-
hide_custom_tags.each do |full_pattern|
|
25
|
+
hide_custom_tags.each do |full_pattern, value|
|
20
26
|
paragraphs = document.css('w|p')
|
21
27
|
while paragraph = paragraphs.shift do
|
22
28
|
next unless paragraph.text =~ full_pattern
|
23
29
|
run_nodes = paragraph.css('w|r')
|
24
30
|
while run_node = run_nodes.shift
|
25
|
-
next
|
26
|
-
|
27
|
-
run_node.
|
28
|
-
|
31
|
+
next unless run_node.at_css('w|t')
|
32
|
+
next unless run_node.text =~ full_pattern
|
33
|
+
tag_contents = split_tag_content(run_node.text, full_pattern)
|
34
|
+
replacement_nodes = []
|
29
35
|
tag_contents[:content_list].each_with_index do |content, idx|
|
36
|
+
remainder_run_node = run_node.clone
|
30
37
|
replace_content(remainder_run_node, content)
|
31
|
-
run_node_with_match = remainder_run_node.dup
|
32
38
|
matched_tag = tag_contents[:matched_tags][idx]
|
33
|
-
|
39
|
+
replacement_nodes << remainder_run_node
|
34
40
|
if matched_tag
|
41
|
+
run_node_with_match = run_node.clone
|
35
42
|
replace_style(run_node_with_match)
|
36
|
-
|
37
|
-
|
43
|
+
matched_content = matched_tag
|
44
|
+
if value
|
45
|
+
matched_content = value.is_a?(Proc) ?
|
46
|
+
value.call(matched_tag) :
|
47
|
+
value.to_s
|
48
|
+
end
|
49
|
+
replace_content(run_node_with_match, matched_content)
|
50
|
+
replacement_nodes << run_node_with_match
|
38
51
|
end
|
39
|
-
paragraph << Nokogiri::XML::NodeSet.new(document, nodes_list)
|
40
|
-
remainder_run_node = remainder_run_node.clone
|
41
52
|
end
|
53
|
+
run_node.add_next_sibling(Nokogiri::XML::NodeSet.new(document, replacement_nodes))
|
54
|
+
run_node.unlink
|
42
55
|
end
|
43
56
|
end
|
44
57
|
end
|
data/lib/lm_docstache/parser.rb
CHANGED
@@ -18,7 +18,22 @@ module LMDocstache
|
|
18
18
|
VARIABLE_MATCHER = /{{([^#\^\/].*?)}}/
|
19
19
|
|
20
20
|
attr_reader :document, :data, :blocks, :special_variable_replacements, :hide_custom_tags
|
21
|
+
attr_reader :data_sequential_replacement
|
21
22
|
|
23
|
+
# Constructor +data+ argument is a +Hash+ where the key is
|
24
|
+
# expected to be a +String+ representing the replacement block value. +Hash+
|
25
|
+
# key must not contain the `{{}}` part, but only the pattern characters.
|
26
|
+
# As for the values of the +Hash+, we have options:
|
27
|
+
#
|
28
|
+
# * +String+ will be the value that will replace matching string.
|
29
|
+
# * +Array<String>+ will be an ordered sequence of values that will replace the matched string following
|
30
|
+
# document matching order.
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# { 'full_name' => 'John Doe', 'text|req|Client' => ['John', 'Matt', 'Paul'] }
|
34
|
+
#
|
35
|
+
# Constructor +options+ argument is a +Hash+ where keys can be:
|
36
|
+
#
|
22
37
|
# The +special_variable_replacements+ option is a +Hash+ where the key is
|
23
38
|
# expected to be either a +Regexp+ or a +String+ representing the pattern
|
24
39
|
# of more specific type of variables that deserves a special treatment. The
|
@@ -34,22 +49,31 @@ module LMDocstache
|
|
34
49
|
# * any other value that will be turned into a string -> in this case, this
|
35
50
|
# will be the value that will replace the matched string
|
36
51
|
#
|
37
|
-
# The +hide_custom_tags+ options is
|
38
|
-
# the pattern you expect to keep at the document but
|
52
|
+
# The +hide_custom_tags+ options is a +Hash+ of +Regexp+ or +String+ keys representing
|
53
|
+
# the pattern you expect to keep at the document but replacing the content to use
|
54
|
+
# font color equal to document background color or white.
|
55
|
+
# For the +Hash+ values we can have:
|
39
56
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
57
|
+
# * +false+ -> In this case we don't change the text content.
|
58
|
+
# * +Proc+ -> When a +Proc+ instance is provided, it's expected it to be
|
59
|
+
# able to receive the matched string and to return the string that will be
|
60
|
+
# used as replacement.
|
61
|
+
# * any other value that will be turned into a string -> in this case, this
|
62
|
+
# will be the value that will replace the matched string
|
43
63
|
def initialize(document, data, options = {})
|
44
64
|
@document = document
|
45
|
-
@data = data.transform_keys(&:to_s)
|
46
|
-
@
|
47
|
-
@
|
65
|
+
@data = data.transform_keys(&:to_s).select {|e, v| !v.is_a?(Array) }
|
66
|
+
@data_sequential_replacement = data.transform_keys(&:to_s).select {|e, v| v.is_a?(Array) }
|
67
|
+
@special_variable_replacements = add_blocks_to_regexp(options.fetch(:special_variable_replacements, {}))
|
68
|
+
@hide_custom_tags = add_blocks_to_regexp(options.fetch(:hide_custom_tags, {}))
|
48
69
|
end
|
49
70
|
|
50
|
-
|
51
|
-
|
52
|
-
|
71
|
+
# Replace +Regepx+ or +String+ keys to have the enclosing blocks
|
72
|
+
def add_blocks_to_regexp(options)
|
73
|
+
options.inject({}) do |x, (regexp_str, value)|
|
74
|
+
key = regexp_str.is_a?(String) ? Regexp.new("{{#{regexp_str}}}") : /{{#{regexp_str.source}}/
|
75
|
+
x[key] = value
|
76
|
+
x
|
53
77
|
end
|
54
78
|
end
|
55
79
|
|
@@ -57,6 +81,7 @@ module LMDocstache
|
|
57
81
|
hide_custom_tags!
|
58
82
|
find_blocks
|
59
83
|
replace_conditional_blocks_in_document!
|
84
|
+
replace_data_sequentially_in_document!
|
60
85
|
replace_variables_in_document!
|
61
86
|
end
|
62
87
|
|
@@ -128,15 +153,39 @@ module LMDocstache
|
|
128
153
|
variable_replacement.call($1) :
|
129
154
|
variable_replacement.to_s
|
130
155
|
end
|
131
|
-
|
132
156
|
text_node.content = text
|
133
157
|
end
|
134
158
|
end
|
135
159
|
|
160
|
+
def replace_data_sequentially_in_document!
|
161
|
+
data_sequential_replacement.each do |tag_key, values|
|
162
|
+
|
163
|
+
tag = Regexp.escape("{{#{tag_key}}}")
|
164
|
+
pattern_found = 0
|
165
|
+
|
166
|
+
document.css('w|t').each do |text_node|
|
167
|
+
text = text_node.text
|
168
|
+
|
169
|
+
if text.match(tag)
|
170
|
+
|
171
|
+
text.gsub!(/#{tag}/) do |_match|
|
172
|
+
value = values[pattern_found]
|
173
|
+
# if there is no more available value replace the content with empty string
|
174
|
+
return '' unless value
|
175
|
+
|
176
|
+
pattern_found +=1
|
177
|
+
value
|
178
|
+
end
|
179
|
+
|
180
|
+
text_node.content = text
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
136
186
|
def has_skippable_variable?(text)
|
137
|
-
return true if hide_custom_tags.find { |pattern| text =~ pattern }
|
187
|
+
return true if hide_custom_tags.find { |(pattern, value)| text =~ pattern }
|
138
188
|
!!special_variable_replacements.find do |(pattern, value)|
|
139
|
-
pattern = pattern.is_a?(String) ? /{{#{pattern}}}/ : /{{#{pattern.source}}}/
|
140
189
|
text =~ pattern && value == false
|
141
190
|
end
|
142
191
|
end
|
@@ -144,7 +193,6 @@ module LMDocstache
|
|
144
193
|
def special_variable_replacement(text)
|
145
194
|
Array(
|
146
195
|
special_variable_replacements.find do |(pattern, value)|
|
147
|
-
pattern = pattern.is_a?(String) ? /{{#{pattern}}}/ : /{{#{pattern.source}}}/
|
148
196
|
text =~ pattern && !!value
|
149
197
|
end
|
150
198
|
).last
|
@@ -6,7 +6,8 @@ module LMDocstache
|
|
6
6
|
|
7
7
|
def initialize(xml, data, options = {})
|
8
8
|
@content = xml
|
9
|
-
|
9
|
+
option_types = [:special_variable_replacements, :hide_custom_tags]
|
10
|
+
@parser = Parser.new(xml, data, options.slice(*option_types))
|
10
11
|
end
|
11
12
|
|
12
13
|
def render
|
data/lib/lm_docstache/version.rb
CHANGED
Binary file
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe LMDocstache::HideCustomTags do
|
4
|
-
|
5
4
|
context '#example' do
|
6
5
|
let(:output_dir) { "#{base_path}/tmp/" }
|
7
6
|
let(:output_file) { File.new("#{output_dir}/BlankTestOutput.docx", 'w') }
|
@@ -16,10 +15,18 @@ describe LMDocstache::HideCustomTags do
|
|
16
15
|
end
|
17
16
|
|
18
17
|
let(:base_path) { SPEC_BASE_PATH.join('example_input') }
|
19
|
-
let(:document) {
|
20
|
-
|
18
|
+
let(:document) {
|
19
|
+
doc = LMDocstache::Document.new(input_file)
|
20
|
+
doc.fix_errors
|
21
|
+
doc.instance_variable_get(:@document)
|
22
|
+
}
|
23
|
+
let(:regexp_tag) { /(?:sig|sigfirm|date|text|initial)\|(?:req|noreq)\|.+?/ }
|
24
|
+
let(:regexp_for_replacement) { /(?:check)\|(?:req|noreq)\|.+?/ }
|
21
25
|
let(:hide_custom_tags) {
|
22
|
-
LMDocstache::HideCustomTags.new(document: document, hide_custom_tags:
|
26
|
+
LMDocstache::HideCustomTags.new(document: document, hide_custom_tags: {
|
27
|
+
/#{regexp_tag}/ => false,
|
28
|
+
/#{regexp_for_replacement}/ => 'replaced_content'
|
29
|
+
})
|
23
30
|
}
|
24
31
|
|
25
32
|
context "giving a document with blue background" do
|
@@ -30,10 +37,9 @@ describe LMDocstache::HideCustomTags do
|
|
30
37
|
d = hide_custom_tags.document
|
31
38
|
run_nodes = d.css('w|p w|r')
|
32
39
|
while run_node = run_nodes.shift
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
40
|
+
next unless run_node.text =~ regexp_tag
|
41
|
+
expect(run_node.at_css('w|rPr w|color').first[1]).to eq('4472C4')
|
42
|
+
expect(run_node.children.first.name).to eq('rPr')
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
@@ -46,14 +52,13 @@ describe LMDocstache::HideCustomTags do
|
|
46
52
|
d = hide_custom_tags.document
|
47
53
|
run_nodes = d.css('w|p w|r')
|
48
54
|
while run_node = run_nodes.shift
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
55
|
+
next unless run_node.text =~ regexp_tag
|
56
|
+
expect(run_node.at_css('w|rPr w|color').first[1]).to eq('FFFFFF')
|
57
|
+
expect(run_node.children.first.name).to eq('rPr')
|
53
58
|
end
|
54
59
|
end
|
55
60
|
end
|
56
|
-
context 'giving a document without rpr' do
|
61
|
+
context 'giving a document without rpr and block tags on the left' do
|
57
62
|
let(:input_file) { "#{base_path}/docx-no-rpr.docx" }
|
58
63
|
|
59
64
|
it 'expect to have a white color on all hide custom tags matching and have first child node equal rPr tag' do
|
@@ -61,12 +66,33 @@ describe LMDocstache::HideCustomTags do
|
|
61
66
|
d = hide_custom_tags.document
|
62
67
|
run_nodes = d.css('w|p w|r')
|
63
68
|
while run_node = run_nodes.shift
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
69
|
+
next unless run_node.text =~ regexp_tag
|
70
|
+
expect(run_node.at_css('w|rPr w|color').first[1]).to eq('FFFFFF')
|
71
|
+
expect(run_node.children.first.name).to eq('rPr')
|
68
72
|
end
|
69
73
|
end
|
74
|
+
it 'expect to have a white color on all replacement tags and content following replacement' do
|
75
|
+
hide_custom_tags.hide_custom_tags!
|
76
|
+
d = hide_custom_tags.document
|
77
|
+
run_nodes = d.css('w|p w|r')
|
78
|
+
total_replacement = 0
|
79
|
+
while run_node = run_nodes.shift
|
80
|
+
next unless run_node.text =~ /replaced_content/
|
81
|
+
total_replacement+=1
|
82
|
+
expect(run_node.at_css('w|rPr w|color').first[1]).to eq('FFFFFF')
|
83
|
+
expect(run_node.children.first.name).to eq('rPr')
|
84
|
+
end
|
85
|
+
expect(total_replacement).to eq(2)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'giving a document with tabs spacing in the middle of replacement tags' do
|
90
|
+
let(:input_file) { "#{base_path}/sample-signature-with-tabs-spacing.docx" }
|
91
|
+
it 'expect to not replace tabs' do
|
92
|
+
hide_custom_tags.hide_custom_tags!
|
93
|
+
d = hide_custom_tags.document
|
94
|
+
expect(d.css('w|p w|tab').size).to eq(11)
|
95
|
+
end
|
70
96
|
end
|
71
97
|
end
|
72
98
|
end
|
data/spec/integration_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
2
3
|
require 'active_support/core_ext/object/blank.rb'
|
3
4
|
|
4
5
|
module LMDocstache
|
@@ -139,5 +140,30 @@ describe 'integration test', integration: true do
|
|
139
140
|
expect(output).to include('<w:t xml:space="preserve">Test Multiple text in the same line </w:t>')
|
140
141
|
end
|
141
142
|
end
|
143
|
+
|
144
|
+
context "yoooo" do
|
145
|
+
let(:input_file) { "#{base_path}/multi_o.docx" }
|
146
|
+
let(:render_options) {
|
147
|
+
{
|
148
|
+
special_variable_replacements: { "(date|sig|sigfirm|text|check|initial|initials)\\|(req|noreq)\\|(.+?)" => false }.freeze,
|
149
|
+
hide_custom_tags: ['(?:sig|sigfirm|date|check|text|initial)\|(?:req|noreq)\|.+?']
|
150
|
+
}
|
151
|
+
}
|
152
|
+
let(:document) { LMDocstache::Document.new(input_file) }
|
153
|
+
|
154
|
+
it 'should have content replacement aligned with hide custom tags' do
|
155
|
+
doc = document
|
156
|
+
doc.fix_errors
|
157
|
+
new_file_path = "#{Time.now.to_i}-#{SecureRandom.uuid}.docx"
|
158
|
+
n = doc.render_file(new_file_path, { 'full_name' => 'fred document01' }, render_options)
|
159
|
+
noko = doc.render_xml({ 'full_name' => 'fred document01' }, render_options)
|
160
|
+
output = noko['word/document.xml'].to_xml
|
161
|
+
#puts output
|
162
|
+
#doc.render_file(new_file_path, { 'full_name' => 'fred document01' }, render_options)
|
163
|
+
#noko = doc.render_xml({ 'full_name' => 'fred document01' }, render_options)
|
164
|
+
#output = noko['word/document.xml'].to_xml
|
165
|
+
#puts output
|
166
|
+
end
|
167
|
+
end
|
142
168
|
end
|
143
169
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lm_docstache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roey Chasman
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2021-
|
15
|
+
date: 2021-05-14 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: nokogiri
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- spec/example_input/blank.docx
|
124
124
|
- spec/example_input/docx-no-rpr.docx
|
125
125
|
- spec/example_input/sample-signature-blue.docx
|
126
|
+
- spec/example_input/sample-signature-with-tabs-spacing.docx
|
126
127
|
- spec/example_input/sample-signature.docx
|
127
128
|
- spec/hide_custom_tags_spec.rb
|
128
129
|
- spec/integration_spec.rb
|
@@ -157,6 +158,7 @@ test_files:
|
|
157
158
|
- spec/example_input/blank.docx
|
158
159
|
- spec/example_input/docx-no-rpr.docx
|
159
160
|
- spec/example_input/sample-signature-blue.docx
|
161
|
+
- spec/example_input/sample-signature-with-tabs-spacing.docx
|
160
162
|
- spec/example_input/sample-signature.docx
|
161
163
|
- spec/hide_custom_tags_spec.rb
|
162
164
|
- spec/integration_spec.rb
|