ad_hoc_template 0.0.1 → 0.1.0
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/.gitignore +2 -0
- data/Gemfile +1 -1
- data/README.md +1 -2
- data/ad_hoc_template.gemspec +1 -1
- data/lib/ad_hoc_template/command_line_interface.rb +94 -7
- data/lib/ad_hoc_template/parser.rb +160 -0
- data/lib/ad_hoc_template/record_reader.rb +281 -0
- data/lib/ad_hoc_template/version.rb +1 -1
- data/lib/ad_hoc_template.rb +29 -176
- data/spec/ad_hoc_template_spec.rb +308 -141
- data/spec/command_line_interface_spec.rb +393 -34
- data/spec/parser_spec.rb +399 -0
- data/spec/record_reader_spec.rb +377 -0
- metadata +11 -6
data/lib/ad_hoc_template.rb
CHANGED
@@ -1,170 +1,8 @@
|
|
1
1
|
require "ad_hoc_template/version"
|
2
|
-
require "
|
3
|
-
require "
|
2
|
+
require "ad_hoc_template/parser"
|
3
|
+
require "ad_hoc_template/record_reader"
|
4
4
|
|
5
5
|
module AdHocTemplate
|
6
|
-
class Parser < TreeStack
|
7
|
-
class TagNode < Parser::Node
|
8
|
-
attr_reader :type
|
9
|
-
|
10
|
-
def push(node=TreeStack::Node.new)
|
11
|
-
node[0] = assign_type(node[0]) if self.empty?
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
def assign_type(first_leaf)
|
16
|
-
return first_leaf unless first_leaf.kind_of? String and /^\S/o.match(first_leaf)
|
17
|
-
@type, first_leaf_content = first_leaf.split(/\s+/o, 2)
|
18
|
-
first_leaf_content||""
|
19
|
-
end
|
20
|
-
private :assign_type
|
21
|
-
|
22
|
-
def contains_any_value_assigned_tag_node?(record)
|
23
|
-
self.select {|n| n.kind_of?(TagNode) }.each do |node|
|
24
|
-
val = record[node.join.strip]
|
25
|
-
return true if val and not val.empty?
|
26
|
-
end
|
27
|
-
false
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class IterationTagNode < TagNode; end
|
32
|
-
class Leaf < Parser::Leaf; end
|
33
|
-
|
34
|
-
HEAD, TAIL = {}, {}
|
35
|
-
|
36
|
-
[[TagNode, "<%", "%>"],
|
37
|
-
[IterationTagNode, "<%#", "#%>"]].each do |type, head, tail|
|
38
|
-
HEAD[head] = type
|
39
|
-
TAIL[tail] = type
|
40
|
-
end
|
41
|
-
|
42
|
-
TOKEN_PAT = PseudoHiki.compile_token_pat(HEAD.keys, TAIL.keys)
|
43
|
-
|
44
|
-
def self.split_into_tokens(str)
|
45
|
-
tokens = []
|
46
|
-
|
47
|
-
while m = TOKEN_PAT.match(str)
|
48
|
-
tokens.push m.pre_match unless m.pre_match.empty?
|
49
|
-
tokens.push m[0]
|
50
|
-
str = m.post_match
|
51
|
-
end
|
52
|
-
|
53
|
-
tokens.push str unless str.empty?
|
54
|
-
tokens
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.parse(str)
|
58
|
-
new(str).parse.tree
|
59
|
-
end
|
60
|
-
|
61
|
-
def initialize(str)
|
62
|
-
@tokens = Parser.split_into_tokens(str)
|
63
|
-
super()
|
64
|
-
end
|
65
|
-
|
66
|
-
def parse
|
67
|
-
while token = @tokens.shift
|
68
|
-
next if TAIL[token] == current_node.class and self.pop
|
69
|
-
next if HEAD[token] and self.push HEAD[token].new
|
70
|
-
self.push Leaf.create(token)
|
71
|
-
end
|
72
|
-
|
73
|
-
self
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
module RecordReader
|
78
|
-
SEPARATOR = /:\s*/o
|
79
|
-
BLOCK_HEAD = /\A\/\/@/o
|
80
|
-
EMPTY_LINE = /\A\r?\n\Z/o
|
81
|
-
ITERATION_MARK = /\A#/o
|
82
|
-
|
83
|
-
def self.remove_leading_empty_lines(lines)
|
84
|
-
until lines.empty? or /\S/o.match(lines.first)
|
85
|
-
lines.shift
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.strip_blank_lines(block)
|
90
|
-
remove_leading_empty_lines(block)
|
91
|
-
block.pop while not block.empty? and EMPTY_LINE.match(block.last)
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.read_key_value_list(lines, record)
|
95
|
-
while line = lines.shift and not EMPTY_LINE.match(line)
|
96
|
-
key, val = line.chomp.split(SEPARATOR, 2)
|
97
|
-
record[key] = val
|
98
|
-
end
|
99
|
-
|
100
|
-
record
|
101
|
-
end
|
102
|
-
|
103
|
-
def self.read_block(lines, record, block_head)
|
104
|
-
block = []
|
105
|
-
|
106
|
-
while line = lines.shift
|
107
|
-
if m = BLOCK_HEAD.match(line)
|
108
|
-
strip_blank_lines(block)
|
109
|
-
record[block_head] = block.join
|
110
|
-
return m.post_match.chomp
|
111
|
-
end
|
112
|
-
|
113
|
-
block.push(line)
|
114
|
-
end
|
115
|
-
|
116
|
-
strip_blank_lines(block)
|
117
|
-
record[block_head] = block.join
|
118
|
-
end
|
119
|
-
|
120
|
-
def self.read_block_part(lines, record, block_head)
|
121
|
-
until lines.empty? or not block_head
|
122
|
-
block_head = read_block(lines, record, block_head)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def self.read_iteration_block(lines, record, block_head)
|
127
|
-
records = []
|
128
|
-
|
129
|
-
while line = lines.shift
|
130
|
-
if m = BLOCK_HEAD.match(line)
|
131
|
-
record[block_head] = records
|
132
|
-
return m.post_match.chomp
|
133
|
-
elsif EMPTY_LINE.match(line)
|
134
|
-
next
|
135
|
-
else
|
136
|
-
lines.unshift line
|
137
|
-
records.push read_key_value_list(lines, {})
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
record[block_head] = records
|
142
|
-
nil
|
143
|
-
end
|
144
|
-
|
145
|
-
def self.read_iteration_block_part(lines, record, block_head)
|
146
|
-
while not lines.empty? and block_head and ITERATION_MARK.match(block_head)
|
147
|
-
block_head = read_iteration_block(lines, record, block_head)
|
148
|
-
end
|
149
|
-
|
150
|
-
block_head
|
151
|
-
end
|
152
|
-
|
153
|
-
def self.read_record(input)
|
154
|
-
lines = input.each_line.to_a
|
155
|
-
record = read_key_value_list(lines, {})
|
156
|
-
remove_leading_empty_lines(lines)
|
157
|
-
|
158
|
-
unless lines.empty?
|
159
|
-
m = BLOCK_HEAD.match(lines.shift)
|
160
|
-
block_head = read_iteration_block_part(lines, record, m.post_match.chomp)
|
161
|
-
read_block_part(lines, record, block_head) if block_head
|
162
|
-
end
|
163
|
-
|
164
|
-
record
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
6
|
class DefaultTagFormatter
|
169
7
|
def find_function(tag_type)
|
170
8
|
FUNCTION_TABLE[tag_type]||:default
|
@@ -178,7 +16,7 @@ module AdHocTemplate
|
|
178
16
|
record[var]||"[#{var}]"
|
179
17
|
end
|
180
18
|
|
181
|
-
def html_encode(var
|
19
|
+
def html_encode(var, record)
|
182
20
|
HtmlElement.escape(record[var]||var)
|
183
21
|
end
|
184
22
|
|
@@ -188,16 +26,24 @@ module AdHocTemplate
|
|
188
26
|
}
|
189
27
|
end
|
190
28
|
|
191
|
-
class
|
192
|
-
def self.
|
193
|
-
|
194
|
-
|
195
|
-
|
29
|
+
class DataLoader
|
30
|
+
def self.format(template, record, tag_formatter=DefaultTagFormatter.new)
|
31
|
+
if record.kind_of? Array
|
32
|
+
return format_multi_records(template, record, tag_formatter)
|
33
|
+
end
|
34
|
+
new(record, tag_formatter).format(template)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.format_multi_records(template, records,
|
38
|
+
tag_formatter=DefaultTagFormatter.new)
|
39
|
+
records.map do |record|
|
40
|
+
new(record, tag_formatter).format(template)
|
41
|
+
end.join
|
196
42
|
end
|
197
43
|
|
198
|
-
def initialize(record,
|
44
|
+
def initialize(record, tag_formatter=DefaultTagFormatter.new)
|
199
45
|
@record = record
|
200
|
-
@
|
46
|
+
@tag_formatter = tag_formatter
|
201
47
|
end
|
202
48
|
|
203
49
|
def visit(tree)
|
@@ -214,13 +60,13 @@ module AdHocTemplate
|
|
214
60
|
end
|
215
61
|
|
216
62
|
def format_iteration_tag(tag_node)
|
217
|
-
sub_records = @record[
|
63
|
+
sub_records = @record[tag_node.type]||[@record]
|
218
64
|
tag_node = Parser::TagNode.new.concat(tag_node.clone)
|
219
65
|
|
220
66
|
sub_records.map do |record|
|
221
67
|
if tag_node.contains_any_value_assigned_tag_node?(record)
|
222
|
-
|
223
|
-
tag_node.map {|leaf| leaf.accept(
|
68
|
+
data_loader = AdHocTemplate::DataLoader.new(record, @tag_formatter)
|
69
|
+
tag_node.map {|leaf| leaf.accept(data_loader) }.join
|
224
70
|
else
|
225
71
|
"".freeze
|
226
72
|
end
|
@@ -229,11 +75,18 @@ module AdHocTemplate
|
|
229
75
|
|
230
76
|
def format_tag(tag_node)
|
231
77
|
leafs = tag_node.map {|leaf| leaf.accept(self) }
|
232
|
-
@
|
78
|
+
@tag_formatter.format(tag_node.type, leafs.join.strip, @record)
|
233
79
|
end
|
234
80
|
|
235
81
|
def format(tree)
|
236
82
|
tree.accept(self).join
|
237
83
|
end
|
238
84
|
end
|
85
|
+
|
86
|
+
def self.convert(record_data, template, tag_type=:default, data_format=:default,
|
87
|
+
tag_formatter=DefaultTagFormatter.new)
|
88
|
+
tree = Parser.parse(template, tag_type)
|
89
|
+
record = RecordReader.read_record(record_data, data_format)
|
90
|
+
DataLoader.format(tree, record, tag_formatter)
|
91
|
+
end
|
239
92
|
end
|