docstache 0.0.3 → 0.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 +26 -0
- data/lib/docstache/block.rb +56 -0
- data/lib/docstache/document.rb +1 -1
- data/lib/docstache/renderer.rb +50 -121
- data/lib/docstache/version.rb +1 -1
- data/lib/docstache.rb +1 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 498cab83d9bdadd7f60741682832abff60440b8b
|
4
|
+
data.tar.gz: 7ae7842084c4e2f9d32e06df3d4892664975dd68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33dcab26cfadbc1c6af24fb888faa084c7a31e00abdc2975b1ddab8a4946d4c684063d764b41e14efb0f2973b70e2d8c5229887fffef6b3288c602c8669be27e
|
7
|
+
data.tar.gz: c8f53d99e0b6c00d23533f046826ec9fd042b0678e5c09d8a1df5ea618dd0d0eaf90989510039dff02db27c8cd0fd4eecc94037d4ce4bbc55cdd79098bd515bd
|
data/changelog.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.0.4
|
4
|
+
|
5
|
+
* Rewrite of the Renderer class
|
6
|
+
* Conditional Blocks now work
|
7
|
+
* Loops now work
|
8
|
+
|
9
|
+
## 0.0.3
|
10
|
+
|
11
|
+
* Added ability to have nested tags like `{{purchase_order.number}}` through the
|
12
|
+
use of the `Docstache::DataScope` class
|
13
|
+
|
14
|
+
## 0.0.2
|
15
|
+
|
16
|
+
* Fixed a bug that made appended documents not get appended to the *end* of a
|
17
|
+
document
|
18
|
+
* Fixed a bug that made merged documents not open in Word. This was caused due
|
19
|
+
to the `section properties` tag in the xml getting used more than once, since
|
20
|
+
each document had one. Now only the first `sectPr` tag is kept.
|
21
|
+
|
22
|
+
## 0.0.1
|
23
|
+
|
24
|
+
* Initial release. Still not working:
|
25
|
+
* Nested tags `{{foo.bar}}`
|
26
|
+
* Blocks (loops and conditiontals) `{{#foo}} ... {{/foo}}`
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Docstache
|
2
|
+
class Block
|
3
|
+
attr_reader :name, :opening_element, :content_elements, :closing_element, :inverted
|
4
|
+
def initialize(name:, data:, opening_element:, content_elements:, closing_element:, inverted:)
|
5
|
+
@name = name
|
6
|
+
@data = data
|
7
|
+
@opening_element = opening_element
|
8
|
+
@content_elements = content_elements
|
9
|
+
@closing_element = closing_element
|
10
|
+
@inverted = inverted
|
11
|
+
end
|
12
|
+
|
13
|
+
def type
|
14
|
+
@type ||= if @inverted
|
15
|
+
:conditional
|
16
|
+
else
|
17
|
+
if @data.get(@name).is_a? Array
|
18
|
+
:loop
|
19
|
+
else
|
20
|
+
:conditional
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def loop?
|
26
|
+
type == :loop
|
27
|
+
end
|
28
|
+
|
29
|
+
def conditional?
|
30
|
+
type == :conditional
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.find_all(name:, data:, elements:, inverted:)
|
34
|
+
if elements.text.match(/\{\{#{inverted ? '\^' : '\#'}#{name}\}\}.+\{\{\/#{name}\}\}/m)
|
35
|
+
if elements.any? { |e| e.text.match(/\{\{#{inverted ? '\^' : '\#'}#{name}\}\}.+\{\{\/#{name}\}\}/m) }
|
36
|
+
matches = elements.select { |e| e.text.match(/\{\{#{inverted ? '\^' : '\#'}#{name}\}\}.+\{\{\/#{name}\}\}/m) }
|
37
|
+
finds = matches.map { |match| find_all(name: name, data: data, elements: match.elements, inverted: inverted) }.flatten
|
38
|
+
return finds
|
39
|
+
else
|
40
|
+
opening = elements.select { |e| e.text.match(/\{\{#{inverted ? '\^' : '\#'}#{name}\}\}/) }.first
|
41
|
+
content = []
|
42
|
+
next_sibling = opening.next
|
43
|
+
while !next_sibling.text.match(/\{\{\/#{name}\}\}/)
|
44
|
+
content << next_sibling
|
45
|
+
next_sibling = next_sibling.next
|
46
|
+
end
|
47
|
+
closing = next_sibling
|
48
|
+
return Block.new(name: name, data: data, opening_element: opening, content_elements: content, closing_element: closing, inverted: inverted)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
raise "Block not found in given elements"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/lib/docstache/document.rb
CHANGED
data/lib/docstache/renderer.rb
CHANGED
@@ -1,151 +1,80 @@
|
|
1
1
|
module Docstache
|
2
2
|
class Renderer
|
3
|
+
BLOCK_REGEX = /\{\{([\#\^])([\w\.]+)\}\}.+?\{\{\/\g<2>\}\}/m
|
4
|
+
|
3
5
|
def initialize(xml, data)
|
4
6
|
@content = xml
|
5
7
|
@data = DataScope.new(data)
|
6
8
|
end
|
7
9
|
|
8
10
|
def render
|
9
|
-
|
11
|
+
find_and_expand_blocks
|
12
|
+
replace_tags(@content, @data)
|
10
13
|
return @content
|
11
14
|
end
|
12
15
|
|
13
16
|
private
|
14
17
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def extract_end_row(nd, key)
|
24
|
-
if !nd.nil?
|
25
|
-
case nd.text.to_s
|
26
|
-
when /\{\{\/#{key.to_s}\}\}/
|
27
|
-
puts "Found End Row for #{key.to_s}"
|
28
|
-
return nd
|
29
|
-
else
|
30
|
-
return extract_end_row(nd.next, key)
|
31
|
-
end
|
32
|
-
else
|
33
|
-
return nil
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def expand_loop(nd, end_nd, key, element)
|
38
|
-
out = []
|
39
|
-
case nd.text.to_s
|
40
|
-
when /\{\{\##{key.to_s}\}\}/
|
41
|
-
out = expand_loop(nd.next, end_nd, key, element)
|
42
|
-
when end_nd.text.to_s
|
43
|
-
out = []
|
44
|
-
when /\{\{\#([a-zA-Z0-9_\.]+)\}\}/
|
45
|
-
new_key = $1
|
46
|
-
out += process_loop(nd, new_key, element)
|
47
|
-
else
|
48
|
-
new_node = nd.dup
|
49
|
-
puts "Adding Row #{nd.text} to list"
|
50
|
-
parse_content(new_node.elements, element)
|
51
|
-
out << new_node
|
52
|
-
puts "Next Node is: #{nd.next.text.to_s}"
|
53
|
-
out += expand_loop(nd.next, end_nd, key, element)
|
18
|
+
def find_and_expand_blocks
|
19
|
+
blocks = @content.text.scan(BLOCK_REGEX)
|
20
|
+
found_blocks = blocks.uniq.map { |block|
|
21
|
+
inverted = block[0] == "^"
|
22
|
+
Block.find_all(name: block[1], elements: @content.elements, data: @data, inverted: inverted)
|
23
|
+
}.flatten
|
24
|
+
found_blocks.each do |block|
|
25
|
+
expand_and_replace_block(block)
|
54
26
|
end
|
55
|
-
return out
|
56
27
|
end
|
57
28
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
case
|
62
|
-
when
|
63
|
-
|
29
|
+
def expand_and_replace_block(block)
|
30
|
+
case block.type
|
31
|
+
when :conditional
|
32
|
+
case condition = @data.get(block.name)
|
33
|
+
when Array
|
34
|
+
condition = !condition.empty?
|
64
35
|
else
|
65
|
-
|
66
|
-
nd.unlink
|
36
|
+
condition = !!condition
|
67
37
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# data_set = data[key]
|
85
|
-
# puts "Expanding Rows for loop #{key.to_s}"
|
86
|
-
# puts "Data count is #{data_set.count}"
|
87
|
-
# puts "Data is #{data_set}"
|
88
|
-
#
|
89
|
-
# data_set.each do |element|
|
90
|
-
# out += expand_loop(nd, end_row, key, element)
|
91
|
-
# end
|
92
|
-
# return out
|
93
|
-
# end
|
94
|
-
end
|
95
|
-
|
96
|
-
def parse_content(elements, data=@data)
|
97
|
-
elements.each do |nd|
|
98
|
-
case nd.name
|
99
|
-
when "tr"
|
100
|
-
case nd.text.to_s
|
101
|
-
when /\{\{\#([a-zA-Z0-9_\.]+)\}\}/
|
102
|
-
key = $1
|
103
|
-
# Get elements to add
|
104
|
-
elements = process_loop(nd, key, data)
|
105
|
-
# Add elements
|
106
|
-
elements.reverse.each do |e|
|
107
|
-
puts "Adding Row to file: #{e.text.to_s}"
|
108
|
-
nd.add_next_sibling(e)
|
109
|
-
end
|
110
|
-
else # it's a normal table row
|
111
|
-
parse_content(nd.elements, data)
|
38
|
+
condition = !condition if block.inverted
|
39
|
+
unless condition
|
40
|
+
block.content_elements.each(&:unlink)
|
41
|
+
end
|
42
|
+
when :loop
|
43
|
+
set = @data.get(block.name)
|
44
|
+
content = set.map { |item|
|
45
|
+
data = DataScope.new(item, @data)
|
46
|
+
elements = block.content_elements.map(&:clone)
|
47
|
+
replace_tags(Nokogiri::XML::NodeSet.new(@content, elements), data)
|
48
|
+
}
|
49
|
+
content.each do |els|
|
50
|
+
el = els[0]
|
51
|
+
els[1..-1].each do |next_el|
|
52
|
+
el.after(next_el)
|
53
|
+
el = next_el
|
112
54
|
end
|
113
|
-
|
114
|
-
subst_content(nd, data)
|
115
|
-
else # it's neither a leaf or a loop so let's process it
|
116
|
-
parse_content(nd.elements, data)
|
55
|
+
block.closing_element.before(els[0])
|
117
56
|
end
|
57
|
+
block.content_elements.each(&:unlink)
|
118
58
|
end
|
59
|
+
block.opening_element.unlink
|
60
|
+
block.closing_element.unlink
|
119
61
|
end
|
120
62
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
nd.unlink
|
63
|
+
def replace_tags(elements, data)
|
64
|
+
elements.css('w|t').each do |text_el|
|
65
|
+
if !(results = text_el.text.scan(/\{\{([\w\.]+)\}\}/).flatten).empty?
|
66
|
+
rendered_string = text_el.text
|
67
|
+
results.each do |r|
|
68
|
+
rendered_string.gsub!(/\{\{#{r}\}\}/, text(data.get(r)))
|
69
|
+
end
|
70
|
+
text_el.content = rendered_string
|
130
71
|
end
|
131
72
|
end
|
73
|
+
return elements
|
132
74
|
end
|
133
75
|
|
134
|
-
def
|
135
|
-
|
136
|
-
keys = nd.text.scan(/\{\{([a-zA-Z0-9_\.]+)\}\}/).map(&:first)
|
137
|
-
keys.each do |key|
|
138
|
-
value = data.get(key)
|
139
|
-
puts "Substituting {{#{key.to_s}}} with #{value}"
|
140
|
-
inner.gsub!("{{#{key.to_s}}}", safe(value))
|
141
|
-
end
|
142
|
-
if !keys.empty?
|
143
|
-
nd.inner_html = inner
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def safe(text)
|
148
|
-
text.to_s
|
76
|
+
def text(obj)
|
77
|
+
"#{obj}"
|
149
78
|
end
|
150
79
|
|
151
80
|
end
|
data/lib/docstache/version.rb
CHANGED
data/lib/docstache.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docstache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Will Cosgrove
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -65,8 +65,10 @@ files:
|
|
65
65
|
- LICENSE.txt
|
66
66
|
- README.md
|
67
67
|
- Rakefile
|
68
|
+
- changelog.md
|
68
69
|
- docstache.gemspec
|
69
70
|
- lib/docstache.rb
|
71
|
+
- lib/docstache/block.rb
|
70
72
|
- lib/docstache/data_scope.rb
|
71
73
|
- lib/docstache/document.rb
|
72
74
|
- lib/docstache/renderer.rb
|
@@ -98,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
100
|
version: '0'
|
99
101
|
requirements: []
|
100
102
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.
|
103
|
+
rubygems_version: 2.4.4
|
102
104
|
signing_key:
|
103
105
|
specification_version: 4
|
104
106
|
summary: Merges Hash of Data into Word docx template files using mustache syntax
|
@@ -110,4 +112,3 @@ test_files:
|
|
110
112
|
- spec/integration_spec.rb
|
111
113
|
- spec/spec_helper.rb
|
112
114
|
- spec/template_processor_spec.rb
|
113
|
-
has_rdoc:
|