docx-templater 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjFlNTJkZDEzOTQyZWViMDJmMzVjNTljMjdkYmM0ODgxZDYxMjlkZQ==
5
+ data.tar.gz: !binary |-
6
+ ZTZjYTAwODdmMGM1ZGQzMGFmYTA3M2VjODAxYjQ2YjM2ODcxNTUxMQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ M2M0ZWUyMzI4ZWEwNGYzMjFmMThmY2ExNDRlMmIxODRkOGU4NmNkYWU1MDYw
10
+ OWI1ZGZjNmQ1NjQxZTljODAxYjc1Mzk2M2JlZDVlMDdlOTdmZDQxNzc3MTVl
11
+ NGNiMjBhOTZmYzcxZTg1YTFlNDQxNjI3OWY4M2ZhY2FjOTdjZWY=
12
+ data.tar.gz: !binary |-
13
+ MjU1OWNjOTkzNDMxNWNiYTY4MmQxM2NiMzFkMDBmNTk4ZWY5NDhjZGFjZWRk
14
+ ODcyYjYxMGQ3M2ZiNGQ4NTFlNGYxYWE0NGYzNTAzOTc3ZjI2Y2M0YWJhYTYz
15
+ MmMzNTgzNzRkMzQ5ZmEyZjJhODBiZDMwYjQwOTJiOTg1ODM0ZWQ=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docx-templater.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 DmitryKa
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ # DocxTemplater
2
+
3
+ PURE RUBY GEM (works on Windows)! I tried use ready solutions, but some of them hasn't neccessary functions, other one didn't work in Win.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'docx-templater2'
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install docx_templater
18
+
19
+ ## Usage
20
+
21
+ Can work with:
22
+ - $SOME_VALUE$ : replace template stub (if value for replacing will be provided);
23
+ - $REPEAT:SOME_COLLECTION$
24
+ (at least, one new row)
25
+ $EACH:SOME_VALUE_OF_COLLECTION_ITEM$
26
+ (at least, one new row)
27
+ $UNREPEAT:SOME_COLLECTION$;
28
+
29
+ Repeating commands must be at the separate paragraphs.
30
+ Note, that created template may look good but have bad structure for templater, so you should open docx as a zip archive, open "word/document.xml" and edit it
31
+
32
+ ## Contributing
33
+
34
+ 1. Fork it
35
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
36
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
37
+ 4. Push to the branch (`git push origin my-new-feature`)
38
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ require "docx/version"
2
+ require 'docx/docx_handler'
3
+
4
+
5
+ module Docx
6
+ end
@@ -0,0 +1,49 @@
1
+ require 'zip'
2
+ require 'nokogiri'
3
+ require 'docx/template_handler'
4
+
5
+ module Docx
6
+ class DocxHandler
7
+ include TemplateHandler
8
+
9
+ DOC_XML_ARCHIVE_PATH = 'word/document.xml'
10
+
11
+ def self.open(path, &block)
12
+ self.new(path, &block)
13
+ end
14
+
15
+ def initialize(path, &block)
16
+ @replace = {}
17
+ if block_given?
18
+ @zip = Zip::File.open(path)
19
+ yield(self)
20
+ @zip.close
21
+ else
22
+ @zip = Zip::File.open(path)
23
+ end
24
+ end
25
+
26
+ def insert(arg_hash)
27
+ raise TypeError unless arg_hash.kind_of? Hash
28
+ xml = @zip.read(DOC_XML_ARCHIVE_PATH)
29
+ into_doc = Nokogiri::XML(xml) { |x| x.noent } #TODO what is noent?
30
+ TemplateHandler::insert arg_hash, into_doc
31
+ @replace[DOC_XML_ARCHIVE_PATH] = into_doc.serialize save_with: 0
32
+ end
33
+
34
+ def save(path)
35
+ Zip::File.open(path, Zip::File::CREATE) do |out|
36
+ @zip.each do |entry|
37
+ out.get_output_stream(entry.name) do |o|
38
+ if @replace[entry.name]
39
+ o.write(@replace[entry.name])
40
+ else
41
+ o.write(@zip.read(entry.name))
42
+ end
43
+ end
44
+ end
45
+ end
46
+ @zip.close
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,161 @@
1
+ module Docx
2
+ module TemplateHandler
3
+
4
+ START = 'REPEAT:'
5
+ FINISH = 'UNREPEAT:'
6
+ EACH = 'EACH:'
7
+ PARAGRAPH_XPATH = '//w:p'
8
+ TEXT_NODE_XPATH = './/w:t'
9
+ BUX = '$'
10
+ COLON = ':'
11
+
12
+ PR = 'Pr'
13
+ P = 'p'
14
+ W = 'w'
15
+
16
+ # Now supported only one-depth cycles and cycle begin/end must be in the separate rows
17
+ def self.insert(hash, doc)
18
+ paragraphs = doc.xpath(PARAGRAPH_XPATH)
19
+ paragraphs.each do |paragraph|
20
+ text_nodes = paragraph.xpath(TEXT_NODE_XPATH)
21
+ text_nodes.each do |text_node|
22
+ value = text_node.content
23
+ next unless value[0] == BUX && value[-1] == BUX
24
+ key = value[1..-2]
25
+ if key.include? COLON
26
+ if key.start_with? START
27
+ key.sub! START, FINISH
28
+ fin_value = value.sub START, FINISH
29
+ paragraph, repeat = bound_repeatable_piece(text_node, fin_value)
30
+ elems_for_repeating = key.sub FINISH, ''
31
+ if hash[elems_for_repeating] && (hash[elems_for_repeating].kind_of? Enumerable)
32
+ hash[elems_for_repeating].each do |repeated_elem|
33
+ repeat.each do |repeatable_paragraph|
34
+ dup_par = repeatable_paragraph.dup
35
+ handle_paragraph dup_par, repeated_elem
36
+ paragraph.add_next_sibling dup_par
37
+ paragraph = dup_par
38
+ end
39
+ end
40
+ end
41
+ repeat.each do |par|
42
+ par.remove
43
+ end
44
+ break
45
+ else
46
+ vars = key.split COLON
47
+ obj = hash[vars[0]]
48
+ text_node.content = if obj.is_a? Hash
49
+ obj[vars[1]]
50
+ else
51
+ begin
52
+ obj.send vars[1]
53
+ rescue NoMethodError => e
54
+ value
55
+ end
56
+ end
57
+ end
58
+ else
59
+ text_node.content = hash[key] || value
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+
68
+ def self.handle_paragraph(paragraph, hash)
69
+ paragraph.xpath(TEXT_NODE_XPATH).each do |text_node|
70
+ value = text_node.content
71
+ next unless value[0] == BUX && value[-1] == BUX
72
+ key = value[1..-2]
73
+ if key.include? COLON
74
+ if key.start_with? EACH
75
+ key.sub! EACH, ''
76
+ text_node.content = hash[key] || value
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def self.bound_repeatable_piece(node, fin_value)
83
+ # Tag name consists from prefix (usually, 'w') and name (i.e. 'r', 'rPr' and so on)
84
+ temp_key = paragraph_containing node
85
+ paragraph = temp_key.next_sibling
86
+ temp_key.remove
87
+ #next unless paragraph
88
+ repeat = []
89
+ while true
90
+ result = find_finish paragraph, fin_value
91
+ #if result.kind_of? Array
92
+ # new_children = Nokogiri::XML::NodeSet.new paragraph.document
93
+ # result.each do |node|
94
+ # new_children.push node
95
+ # end
96
+ # paragraph.children = new_children
97
+ # repeat.push paragraph
98
+ # break
99
+ #end
100
+ unless result
101
+ # Move 'paragraph' back and remove ending delimiter
102
+ temp_key = paragraph
103
+ paragraph = paragraph.previous_sibling
104
+ temp_key.remove
105
+ break
106
+ end
107
+ #paragraph.xpath('.//w:t')
108
+ repeat.push result
109
+ paragraph = paragraph.next_sibling
110
+ break unless paragraph
111
+ end
112
+ return paragraph, repeat
113
+ end
114
+
115
+ def self.find_finish(node, finish_str)
116
+
117
+ # Now think that repeating will be bounded by paragraphs
118
+
119
+ return node if node.name.end_with? PR
120
+ children = []
121
+ node.children.each do |child|
122
+ ## Be careful, result = nil in the case, when we found finish_str
123
+ #result = find_finish child, finish_str
124
+ #unless result # '$FINISH:...$' returns nil
125
+ # return nil if children.empty? # <w:t>$...$</w:t> returns nil here
126
+ # return nil if children.size == 1 && node.name+PR == children.first.name # <w:p><w:pPr>...</w:pPr><w:t>...</w:t> returns nil here
127
+ # return children
128
+ #else
129
+ # # If node is returned, then... If array, then...
130
+ # if result.kind_of? Nokogiri::XML::Node
131
+ # children.push child
132
+ # elsif result.kind_of? Array
133
+ # new_children = Nokogiri::XML::NodeSet.new child.document
134
+ # result.each do |res_node|
135
+ # new_children.push res_node
136
+ # end
137
+ # child.children = new_children
138
+ # children.push child
139
+ # return children
140
+ # end
141
+ #end
142
+ end
143
+
144
+ # WARNING: It seems, situation exists, when finish_str willn't contain in the children but will be in the node
145
+ if node.content == finish_str
146
+ return nil
147
+ else
148
+ return node
149
+ end
150
+ end
151
+
152
+
153
+ def self.paragraph_containing node
154
+ return nil unless node.namespace.prefix == W
155
+ return node if node.name == P
156
+ while (node = node.parent).name != P
157
+ end
158
+ node
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,3 @@
1
+ module Docx
2
+ VERSION = "0.0.6"
3
+ end
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14"><w:body><w:p w:rsidR="003E7B44" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t xml:space="preserve">Dear </w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$NAME$</w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>!</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>You want to know my progress, so:</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$REPEAT:ELEMENTS$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:INDEX$</w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t xml:space="preserve">. </w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:NAME$</w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t xml:space="preserve"> - </w:t></w:r><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:NUMBER$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$EACH:COMMENTS$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRPr="00F622F5" w:rsidRDefault="00F622F5" w:rsidP="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>$UNREPEAT:ELEMENTS$</w:t></w:r></w:p><w:p w:rsidR="00F622F5" w:rsidRPr="00F622F5" w:rsidRDefault="00F622F5"><w:pPr><w:rPr><w:lang w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang w:val="en-US"/></w:rPr><w:t>Good luck!</w:t></w:r></w:p><w:sectPr w:rsidR="00F622F5" w:rsidRPr="00F622F5"><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1134" w:right="850" w:bottom="1134" w:left="1701" w:header="708" w:footer="708" w:gutter="0"/><w:cols w:space="708"/><w:docGrid w:linePitch="360"/></w:sectPr></w:body></w:document>
@@ -0,0 +1,14 @@
1
+ require 'docx'
2
+
3
+ SRC_FILE = 'template.docx'
4
+ DST_FILE = 'ready.docx'
5
+
6
+ # Note, in second element 'COMMENTS' is absent. Original stub in template file will be remainded
7
+ hash = {'NAME' => 'Ann', 'ELEMENTS' => [
8
+ { 'NAME' => 'World history', 'INDEX' => '1','NUMBER' => '3', 'COMMENTS' => "Yeah, I'm so stupid in it"},
9
+ {'INDEX' => '2', 'NAME' => 'Russian language', 'NUMBER' => '5'}
10
+ ]}
11
+
12
+ w = Docx::DocxHandler.open(SRC_FILE)
13
+ w.insert(hash)
14
+ w.save(DST_FILE)
Binary file
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docx-templater
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - DmitryKa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubyzip
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
69
+ description: ! 'PURE RUBY GEM (works on Windows)! Can work with:
70
+
71
+ - $SOME_VALUE$;
72
+
73
+ - $REPEAT:SOME_COLLECTION$ (at least, new row) $EACH:SOME_VALUE_OF_COLLECTION_ITEM$
74
+ (at least, new row) $UNREPEAT:SOME_COLLECTION$;
75
+
76
+ Repeating commands must be at the separate paragraphs.
77
+
78
+ Note, that created template may look good but have bad structure for templater,
79
+ so you should open docx as a zip archive, open "word/document.xml" and edit it'
80
+ email:
81
+ - mail.of.dima.ka@gmail.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - lib/docx.rb
92
+ - lib/docx/docx_handler.rb
93
+ - lib/docx/template_handler.rb
94
+ - lib/docx/version.rb
95
+ - lib/example/document.xml
96
+ - lib/example/example.rb
97
+ - lib/example/template.docx
98
+ homepage: ''
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.0.7
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Simple docx templater
122
+ test_files: []