docstache 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|