standard-procedure-consolidate 0.2.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +20 -4
- data/.ruby-version +1 -1
- data/.standard.yml +1 -1
- data/CHANGELOG.md +9 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/checksums/standard-procedure-consolidate-0.3.0.gem.sha512 +1 -0
- data/checksums/standard-procedure-consolidate-0.3.1.gem.sha512 +1 -0
- data/lib/consolidate/docx/merge.rb +84 -44
- data/lib/consolidate/version.rb +1 -1
- metadata +8 -7
- data/.nova/Configuration.json +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7a086625fa1f07169c2f9eba544538df266fbd83702efeedd13998d1a9e83b3
|
4
|
+
data.tar.gz: f80f0c11aa839c9dba9c4dc41758471ccfc31d7d38159e902e11d48cc8016c82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72b26cbc5f54cc481ad49cbb604338591e27f1a503330372ab41920a05f3cbde4b1d6b6aa4b607b1189fbd8fd5ed30102d9741b8b6cf762c0cb39c900ac19327
|
7
|
+
data.tar.gz: 33ab5fbe36794cf24ca73ce70ec6da8f91cfe5430a438d5794da54bd668e72dd956331401ac983f5d15b7f77d60ef8ac6a3d438e4a0fb6279736c6dd79342931
|
@@ -1,7 +1,23 @@
|
|
1
1
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
2
2
|
// README at: https://github.com/devcontainers/templates/tree/main/src/ruby
|
3
3
|
{
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
"name": "Ruby",
|
5
|
+
"image": "mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye",
|
6
|
+
"postCreateCommand": "bundle install",
|
7
|
+
"customizations": {
|
8
|
+
"vscode": {
|
9
|
+
"extensions": [
|
10
|
+
"Shopify.ruby-extensions-pack",
|
11
|
+
"testdouble.vscode-standard-ruby",
|
12
|
+
"manuelpuyol.erb-linter",
|
13
|
+
"Shopify.ruby-lsp",
|
14
|
+
"aki77.rails-db-schema",
|
15
|
+
"miguel-savignano.ruby-symbols",
|
16
|
+
"sibiraj-s.vscode-scss-formatter",
|
17
|
+
"Thadeu.vscode-run-rspec-file",
|
18
|
+
"Cronos87.yaml-symbols",
|
19
|
+
"aliariff.vscode-erb-beautify"
|
20
|
+
]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.2.
|
1
|
+
3.2.5
|
data/.standard.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## [0.3.1] - 2024-11-22
|
2
|
+
|
3
|
+
Ensure that the substituted nodes are reinserted correctly into the output document, attempting to restore formatting at the paragraph level (although it does lose formatting at lower levels than this - so-called "run" nodes which represent arbitrary spans of characters within the paragraph).
|
4
|
+
|
5
|
+
|
6
|
+
## [0.3.0] - 2024-11-21
|
7
|
+
|
8
|
+
Updated the code that examines the docx file for merge fields to deal with Word formatting tags being inserted in the middle of the merge fields.
|
9
|
+
|
1
10
|
## [0.2.0] - 2023-09-13
|
2
11
|
|
3
12
|
Thrown away the mail-merge implementation and replaced it with a simple search/replace.
|
data/README.md
CHANGED
@@ -78,7 +78,7 @@ Consolidate looks for word/document.xml files, plus any files that match word/he
|
|
78
78
|
|
79
79
|
## Development
|
80
80
|
|
81
|
-
The repo contains a .devcontainer folder - this contains instructions for a development container that has everything needed to build the project. Once the container has started, you can use `bin/setup` to install dependencies. Then, run `rake
|
81
|
+
The repo contains a .devcontainer folder - this contains instructions for a development container that has everything needed to build the project. Once the container has started, you can use `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
82
82
|
|
83
83
|
`bundle exec rake install` will install the gem on your local machine (obviously not from within the devcontainer though). To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
84
84
|
|
data/Rakefile
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
098078b13e3bb4e3370cbc6bdb0195300489dfb33a5c27a201a4c5f1f0184bba73557264e674f49da41dbc62ad991e8ae99bef1debf417a5b1a363b1d56829f1
|
@@ -0,0 +1 @@
|
|
1
|
+
ac1393002b538b8f000c1e4f1f7f4514f689434dad0889a77a0470bd839c58cb49d6218daafd4a8ca48675e220ab9c42147de78a40ec0fd59d5a19371b699eb0
|
@@ -11,6 +11,15 @@ module Consolidate
|
|
11
11
|
path
|
12
12
|
end
|
13
13
|
|
14
|
+
def initialize(path, verbose: false, &block)
|
15
|
+
@verbose = verbose
|
16
|
+
@output = {}
|
17
|
+
@zip = Zip::File.open(path)
|
18
|
+
@documents = load_documents
|
19
|
+
block&.call self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Helper method to display the contents of the document and the merge fields from the CLI
|
14
23
|
def examine
|
15
24
|
documents = document_names.join(", ")
|
16
25
|
fields = field_names.join(", ")
|
@@ -18,39 +27,37 @@ module Consolidate
|
|
18
27
|
puts "Merge fields: #{fields}"
|
19
28
|
end
|
20
29
|
|
30
|
+
# Read all documents within the docx and extract any merge fields
|
21
31
|
def field_names
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
field_name = matches[1].strip
|
26
|
-
puts "...field #{field_name} found in #{name}" if verbose
|
27
|
-
field_name
|
28
|
-
end.compact
|
29
|
-
end.flatten
|
32
|
+
tag_nodes.collect do |tag_node|
|
33
|
+
field_names_from tag_node
|
34
|
+
end.flatten.compact.uniq
|
30
35
|
end
|
31
36
|
|
37
|
+
# List the documents stored within this docx
|
32
38
|
def document_names
|
33
39
|
@zip.entries.collect { |entry| entry.name }
|
34
40
|
end
|
35
41
|
|
36
|
-
|
37
|
-
|
42
|
+
# Substitute the data from the merge fields with the values provided
|
43
|
+
def data mapping = {}
|
44
|
+
mapping = mapping.transform_keys(&:to_s)
|
38
45
|
|
39
46
|
if verbose
|
40
47
|
puts "...substitutions..."
|
41
|
-
|
48
|
+
mapping.each do |key, value|
|
42
49
|
puts " #{key} => #{value}"
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
46
53
|
@documents.each do |name, document|
|
47
|
-
|
48
|
-
result = substitute result, fields, name
|
54
|
+
output_document = substitute document.dup, mapping: mapping, document_name: name
|
49
55
|
|
50
|
-
@output[name] =
|
56
|
+
@output[name] = output_document.serialize save_with: 0
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
60
|
+
# Write the new document to the given path
|
54
61
|
def write_to path
|
55
62
|
puts "...writing to #{path}" if verbose
|
56
63
|
Zip::File.open(path, Zip::File::CREATE) do |out|
|
@@ -62,51 +69,84 @@ module Consolidate
|
|
62
69
|
end
|
63
70
|
end
|
64
71
|
|
65
|
-
|
72
|
+
private
|
66
73
|
|
67
74
|
attr_reader :verbose
|
68
75
|
attr_reader :zip
|
69
76
|
attr_reader :xml
|
70
77
|
attr_reader :documents
|
71
78
|
attr_accessor :output
|
79
|
+
TAG = /\{\{\s*(\S+)\s*\}\}/
|
72
80
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
@zip = Zip::File.open(path)
|
80
|
-
@zip.entries.each do |entry|
|
81
|
-
next unless entry.name.match?(/word\/(document|header|footer|footnotes|endnotes).?\.xml/)
|
82
|
-
puts "...reading #{entry.name}" if verbose
|
83
|
-
xml = @zip.get_input_stream entry
|
84
|
-
@documents[entry.name] = Nokogiri::XML(xml) { |x| x.noent }
|
85
|
-
end
|
86
|
-
yield self
|
87
|
-
ensure
|
88
|
-
@zip.close
|
81
|
+
def load_documents
|
82
|
+
@zip.entries.each_with_object({}) do |entry, documents|
|
83
|
+
next unless entry.name.match?(/word\/(document|header|footer|footnotes|endnotes).?\.xml/)
|
84
|
+
puts "...reading #{entry.name}" if verbose
|
85
|
+
xml = @zip.get_input_stream entry
|
86
|
+
documents[entry.name] = Nokogiri::XML(xml) { |x| x.noent }
|
89
87
|
end
|
88
|
+
ensure
|
89
|
+
@zip.close
|
90
90
|
end
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
# Collect all the nodes that contain merge fields
|
93
|
+
def tag_nodes
|
94
|
+
documents.collect do |name, document|
|
95
|
+
tag_nodes_for document
|
96
|
+
end.flatten
|
97
|
+
end
|
98
|
+
|
99
|
+
# go through all w:t (Word Text???) nodes of the document
|
100
|
+
# find any nodes that contain "{{"
|
101
|
+
# then find the ancestor node that also includes the ending "}}"
|
102
|
+
# This collection of nodes contains all the merge fields for this document
|
103
|
+
def tag_nodes_for document
|
104
|
+
(document / "//w:p").select do |paragraph|
|
105
|
+
paragraph.content.match(TAG)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Extract the merge field name from the node
|
110
|
+
def field_names_from(tag_node)
|
111
|
+
matches = tag_node.content.scan(TAG)
|
112
|
+
matches.empty? ? nil : matches.flatten.map(&:strip)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Go through the given document, replacing any merge fields with the values provided
|
116
|
+
# and storing the results in a new document
|
117
|
+
def substitute document, document_name:, mapping: {}
|
118
|
+
tag_nodes_for(document).each do |tag_node|
|
119
|
+
field_names = field_names_from tag_node
|
120
|
+
puts "Original Node for #{field_names} is #{tag_node}" if verbose
|
121
|
+
|
122
|
+
# Extract the paragraph properties node if it exists
|
123
|
+
paragraph_properties = tag_node.search ".//w:pPr"
|
124
|
+
run_properties = tag_node.at_xpath ".//w:rPr"
|
125
|
+
|
126
|
+
text = tag_node.content
|
127
|
+
field_names.each do |field_name|
|
128
|
+
field_value = mapping[field_name].to_s
|
98
129
|
puts "...substituting #{field_name} with #{field_value} in #{document_name}" if verbose
|
99
|
-
|
100
|
-
elsif verbose
|
101
|
-
puts "...found #{field_name} but no replacement value"
|
130
|
+
text = text.gsub(/{{\s*#{field_name}\s*}}/, field_value)
|
102
131
|
end
|
132
|
+
|
133
|
+
# Create a new text node with the substituted text
|
134
|
+
text_node = Nokogiri::XML::Node.new("w:t", tag_node.document)
|
135
|
+
text_node.content = text
|
136
|
+
|
137
|
+
# Create a new run node to hold the substituted text and the paragraph properties
|
138
|
+
run_node = Nokogiri::XML::Node.new("w:r", tag_node.document)
|
139
|
+
run_node << run_properties if run_properties
|
140
|
+
run_node << text_node
|
141
|
+
tag_node.children = Nokogiri::XML::NodeSet.new(document, paragraph_properties.to_a + [run_node])
|
142
|
+
|
143
|
+
puts "TAG NODE FOR #{field_names} IS #{tag_node}" if verbose
|
144
|
+
rescue => ex
|
145
|
+
# Have to mangle the exception message otherwise it outputs the entire document
|
146
|
+
puts ex.message.to_s[0..255]
|
103
147
|
end
|
104
148
|
document
|
105
149
|
end
|
106
|
-
|
107
|
-
def close
|
108
|
-
zip.close
|
109
|
-
end
|
110
150
|
end
|
111
151
|
end
|
112
152
|
end
|
data/lib/consolidate/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standard-procedure-consolidate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rahoul Baruah
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -48,7 +48,6 @@ extensions: []
|
|
48
48
|
extra_rdoc_files: []
|
49
49
|
files:
|
50
50
|
- ".devcontainer/devcontainer.json"
|
51
|
-
- ".nova/Configuration.json"
|
52
51
|
- ".rspec"
|
53
52
|
- ".ruby-version"
|
54
53
|
- ".standard.yml"
|
@@ -62,6 +61,8 @@ files:
|
|
62
61
|
- checksums/standard-procedure-consolidate-0.1.3.gem.sha512
|
63
62
|
- checksums/standard-procedure-consolidate-0.1.4.gem.sha512
|
64
63
|
- checksums/standard-procedure-consolidate-0.2.0.gem.sha512
|
64
|
+
- checksums/standard-procedure-consolidate-0.3.0.gem.sha512
|
65
|
+
- checksums/standard-procedure-consolidate-0.3.1.gem.sha512
|
65
66
|
- exe/consolidate
|
66
67
|
- exe/examine
|
67
68
|
- lib/consolidate.rb
|
@@ -77,7 +78,7 @@ metadata:
|
|
77
78
|
homepage_uri: https://github.com/standard-procedure/standard-procedure-consolidate
|
78
79
|
source_code_uri: https://github.com/standard-procedure/standard-procedure-consolidate
|
79
80
|
changelog_uri: https://github.com/standard-procedure/standard-procedure-consolidate/blob/main/CHANGELOG.md
|
80
|
-
post_install_message:
|
81
|
+
post_install_message:
|
81
82
|
rdoc_options: []
|
82
83
|
require_paths:
|
83
84
|
- lib
|
@@ -92,8 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
93
|
- !ruby/object:Gem::Version
|
93
94
|
version: '0'
|
94
95
|
requirements: []
|
95
|
-
rubygems_version: 3.4.
|
96
|
-
signing_key:
|
96
|
+
rubygems_version: 3.4.19
|
97
|
+
signing_key:
|
97
98
|
specification_version: 4
|
98
99
|
summary: Simple ruby mailmerge for Microsoft Word .docx files.
|
99
100
|
test_files: []
|
data/.nova/Configuration.json
DELETED