ad_hoc_template 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|