ad_hoc_template 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,14 +1,14 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  module AdHocTemplate
4
4
  class DefaultTagFormatter
5
5
  FUNCTION_TABLE = {
6
- "=" => :default,
7
- "h" => :html_encode
6
+ '=' => :default,
7
+ 'h' => :html_encode,
8
8
  }
9
9
 
10
10
  def self.assign_format(format_label, &func)
11
- if format_label.kind_of? Hash and not func
11
+ if format_label.kind_of?(Hash) && !func
12
12
  func_name, label = format_label.to_a.flatten
13
13
  FUNCTION_TABLE[label] = func_name
14
14
  else
@@ -17,25 +17,25 @@ module AdHocTemplate
17
17
  end
18
18
 
19
19
  def find_function(format_label)
20
- FUNCTION_TABLE[format_label]||:default
20
+ FUNCTION_TABLE[format_label] || :default
21
21
  end
22
22
 
23
23
  def format(format_label, var, record)
24
24
  func = find_function(format_label)
25
25
  case func
26
26
  when Symbol, String
27
- self.send(func, var, record)
27
+ send(func, var, record)
28
28
  else
29
29
  func.call(var, record)
30
30
  end
31
31
  end
32
32
 
33
33
  def default(var, record)
34
- record[var]||"[#{var}]"
34
+ record[var] || "[#{var}]"
35
35
  end
36
36
 
37
37
  def html_encode(var, record)
38
- HtmlElement.escape(record[var]||var)
38
+ HtmlElement.escape(record[var] || var)
39
39
  end
40
40
  end
41
41
  end
@@ -1,7 +1,9 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  module AdHocTemplate
4
4
  module EntryFormatGenerator
5
+ DEFAULT_ENCODING = Encoding.default_external.names[0]
6
+
5
7
  class LabelChecker
6
8
  attr_reader :labels
7
9
  def initialize
@@ -22,13 +24,15 @@ module AdHocTemplate
22
24
  private
23
25
 
24
26
  def visit_iteration_tag_node(tree, memo)
25
- if iteration_label = tree.type
27
+ sub_checker = self
28
+ iteration_label = tree.type
29
+
30
+ if iteration_label
26
31
  sub_checker = self.class.new
27
32
  @labels[iteration_label] = [sub_checker.labels]
28
- tree.each { |node| node.accept(sub_checker, memo) }
29
- else
30
- tree.each {|node| node.accept(self, memo) }
31
33
  end
34
+
35
+ tree.each {|node| node.accept(sub_checker, memo) }
32
36
  end
33
37
  end
34
38
 
@@ -39,17 +43,23 @@ module AdHocTemplate
39
43
  RecordReader.dump(labels, target_format)
40
44
  end
41
45
 
42
- def self.extract_recipes_from_template_files(template_paths, tag_type=:default,
43
- encoding=Encoding.default_external.names[0])
44
- recipes = template_paths.map do |path|
45
- full_path = File.expand_path(path)
46
- template_source = open(full_path) {|file| file.read }
47
- extract_recipe(template_source, path, tag_type, encoding)
46
+ def self.extract_recipes_from_template_files(template_paths,
47
+ tag_type=:default,
48
+ encoding=DEFAULT_ENCODING)
49
+ recipes = map_read_files(template_paths, encoding) do |path, src|
50
+ extract_recipe(src, path, tag_type, encoding)
48
51
  end
49
52
 
50
53
  recipes.join
51
54
  end
52
55
 
56
+ def self.map_read_files(paths, encoding=DEFAULT_ENCODING)
57
+ paths.map do |path|
58
+ full_path = File.expand_path(path)
59
+ yield path, File.open(full_path, "rb:BOM|#{encoding}", &:read)
60
+ end
61
+ end
62
+
53
63
  def self.extract_recipe(template_source, template_path,
54
64
  tag_type=:default, encoding='UTF-8')
55
65
  recipe = recipe_entry(template_path, tag_type, encoding)
@@ -91,7 +101,7 @@ module AdHocTemplate
91
101
  end
92
102
 
93
103
  def self.recipe_entry(template_path, tag_type, encoding)
94
- recipe = {
104
+ {
95
105
  'template' => template_path,
96
106
  'tag_type' => tag_type,
97
107
  'template_encoding' => encoding,
@@ -99,7 +109,7 @@ module AdHocTemplate
99
109
  'data_format' => nil,
100
110
  'data_encoding' => nil,
101
111
  'output_file' => nil,
102
- 'blocks' => []
112
+ 'blocks' => [],
103
113
  }
104
114
  end
105
115
 
@@ -112,6 +122,7 @@ module AdHocTemplate
112
122
  }
113
123
  end
114
124
 
125
+ private_class_method :map_read_files
115
126
  private_class_method :extract_form_as_ruby_objects
116
127
  private_class_method :pull_up_inner_iterations
117
128
  private_class_method :each_iteration_label
@@ -1,7 +1,7 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
- require "pseudohiki/inlineparser"
4
- require "htmlelement"
3
+ require 'pseudohiki/inlineparser'
4
+ require 'htmlelement'
5
5
 
6
6
  module AdHocTemplate
7
7
  LINE_END_RE = /(?:\r?\n|\r)/
@@ -13,7 +13,7 @@ module AdHocTemplate
13
13
 
14
14
  def push(node=TreeStack::Node.new)
15
15
  first_leaf = node[0]
16
- node[0] = assign_value_to_type(first_leaf) if empty? and first_leaf
16
+ node[0] = assign_value_to_type(first_leaf) if empty? && first_leaf
17
17
  super
18
18
  end
19
19
 
@@ -27,19 +27,35 @@ module AdHocTemplate
27
27
  each_tag_node {|node| return true if node.contains_any_value_tag? }
28
28
  end
29
29
 
30
+ def contains_any_fallback_tag?
31
+ any? {|sub_node| sub_node.kind_of? Parser::FallbackNode }
32
+ end
33
+
30
34
  def inner_iteration_tag_labels
31
35
  names = []
32
36
  each_tag_node do |node|
33
37
  next unless node.kind_of? IterationNode
34
38
  names.push node.type if node.type
35
- if inner_names = node.inner_iteration_tag_labels
36
- names.concat inner_names
37
- end
39
+ inner_names = node.inner_iteration_tag_labels
40
+ names.concat inner_names if inner_names
38
41
  end
39
42
 
40
43
  names unless names.empty?
41
44
  end
42
45
 
46
+ def cast(node_type=Parser::TagNode)
47
+ node_type.new.concat(clone)
48
+ end
49
+
50
+ def select_fallback_nodes
51
+ nodes = select {|sub_node| sub_node.kind_of? Parser::FallbackNode }
52
+ nodes.empty? ? nil : nodes
53
+ end
54
+
55
+ def format_sub_nodes(data_loader, memo)
56
+ map {|leaf| leaf.accept(data_loader, memo) }.join
57
+ end
58
+
43
59
  private
44
60
 
45
61
  def each_tag_node
@@ -48,11 +64,11 @@ module AdHocTemplate
48
64
  end
49
65
 
50
66
  def assign_value_to_type(first_leaf)
51
- if first_leaf.kind_of? String and /\A\s/ =~ first_leaf
52
- return first_leaf.sub(/\A#{LINE_END_STR}/, "")
67
+ if first_leaf.kind_of?(String) && /\A\s/ =~ first_leaf
68
+ return first_leaf.sub(/\A#{LINE_END_STR}/, '')
53
69
  end
54
70
  @type, first_leaf_content = split_by_newline_or_spaces(first_leaf)
55
- first_leaf_content||""
71
+ first_leaf_content || ''
56
72
  end
57
73
 
58
74
  def split_by_newline_or_spaces(first_leaf)
@@ -62,12 +78,30 @@ module AdHocTemplate
62
78
  end
63
79
 
64
80
  class IterationNode < TagNode
81
+ class InnerLabel
82
+ attr_reader :inner_label
83
+
84
+ def self.labels(inner_labels, cur_label)
85
+ inner_labels.map {|label| new(label, cur_label) }
86
+ end
87
+
88
+ def initialize(inner_label, cur_label)
89
+ @inner_label = inner_label
90
+ @label, @key = inner_label.sub(/\A#/, '').split(/\|/, 2)
91
+ @cur_label = cur_label
92
+ end
93
+
94
+ def full_label(record)
95
+ [@cur_label, @label, record[@key]].join('|')
96
+ end
97
+ end
98
+
65
99
  def assign_value_to_type(first_leaf)
66
100
  return first_leaf unless first_leaf.kind_of? String
67
101
 
68
102
  if /\A[^\s:]*:\s/ =~ first_leaf
69
103
  @type, remaining_part = first_leaf.split(/:(?:#{LINE_END_STR}|\s)/, 2)
70
- @type = @type.empty? ? nil : '#'.freeze + @type
104
+ @type = @type.empty? ? nil : '#' + @type
71
105
  return remaining_part
72
106
  end
73
107
 
@@ -81,13 +115,19 @@ module AdHocTemplate
81
115
  end
82
116
  end
83
117
 
118
+ def inner_labels
119
+ return unless @type
120
+ labels = inner_iteration_tag_labels
121
+ InnerLabel.labels(labels, @type) if labels
122
+ end
123
+
84
124
  private
85
125
 
86
126
  def not_empty_sub_records?(record)
87
127
  sub_records = record[type]
88
- return false if sub_records.nil? or sub_records.empty?
128
+ return false if sub_records.nil? || sub_records.empty?
89
129
  sub_records.each do |rec|
90
- return true if rec.values.any? {|val| val and not val.empty? }
130
+ return true if rec.values.any? {|val| val && !val.empty? }
91
131
  end
92
132
  false
93
133
  end
@@ -99,15 +139,20 @@ module AdHocTemplate
99
139
  first_leaf.sub(/\A#{LINE_END_STR}/, '')
100
140
  end
101
141
 
102
- def contains_any_value_assigned_tag_node?(record)
142
+ def contains_any_value_assigned_tag_node?(_record)
103
143
  false
104
144
  end
145
+
146
+ def format_sub_nodes(data_loader, memo)
147
+ node = cast(Parser::IterationNode)
148
+ node.contains_any_value_tag? ? node.accept(data_loader, memo) : node.join
149
+ end
105
150
  end
106
151
 
107
152
  class ValueNode < TagNode
108
153
  def contains_any_value_assigned_tag_node?(record)
109
- val = record[self.join.strip]
110
- val and not val.empty?
154
+ val = record[join.strip]
155
+ val && !val.empty?
111
156
  end
112
157
 
113
158
  def contains_any_value_tag?
@@ -118,7 +163,34 @@ module AdHocTemplate
118
163
  class Leaf < Parser::Leaf; end
119
164
 
120
165
  class TagType
121
- attr_reader :head, :tail, :token_pat, :remove_iteration_indent
166
+ PREDEFINED = {
167
+ default: [
168
+ ['<%', '%>'], ['<%#', '#%>'],
169
+ ['<%*', '*%>'], false,
170
+ ],
171
+ square_brackets: [
172
+ ['[[', ']]'], ['[[#', '#]]'],
173
+ ['[[*', '*]]'], false,
174
+ ],
175
+ curly_brackets: [
176
+ ['{{', '}}'], ['{{#', '#}}'],
177
+ ['{{*', '*}}'], false,
178
+ ],
179
+ xml_like1: [
180
+ ['<!--%', '%-->'], ['<iterate>', '</iterate>'],
181
+ ['<fallback>', '</fallback>'], true,
182
+ ],
183
+ xml_like2: [
184
+ ['<fill>', '</fill>'], ['<iterate>', '</iterate>'],
185
+ ['<fallback>', '</fallback>'], true,
186
+ ],
187
+ xml_comment_like: [
188
+ ['<!--%', '%-->'], ['<!--%iterate%-->', '<!--%/iterate%-->'],
189
+ ['<!--%fallback%-->', '<!--%/fallback%-->'], true,
190
+ ],
191
+ }.freeze
192
+
193
+ attr_reader :head, :tail, :token_pat, :strip_iteration_indent
122
194
  attr_reader :head_of, :tail_of
123
195
  @types = {}
124
196
 
@@ -126,60 +198,59 @@ module AdHocTemplate
126
198
  @types[tag_name]
127
199
  end
128
200
 
129
- def self.register(tag_name=:default, tag=["<%", "%>"], iteration_tag=["<%#", "#%>"],
130
- fallback_tag=["<%*", "*%>"], remove_iteration_indent=false)
131
- @types[tag_name] = new(tag, iteration_tag, fallback_tag, remove_iteration_indent)
201
+ def self.register(tag_name, tag, iteration_tag,
202
+ fallback_tag, strip_iteration_indent=false)
203
+ @types[tag_name] = new(tag, iteration_tag,
204
+ fallback_tag, strip_iteration_indent)
132
205
  end
133
206
 
134
- def initialize(tag, iteration_tag, fallback_tag, remove_iteration_indent)
207
+ def initialize(tag, iteration_tag, fallback_tag, strip_iteration_indent)
135
208
  assign_type(tag, iteration_tag, fallback_tag)
136
209
  @token_pat = PseudoHiki.compile_token_pat(@head.keys, @tail.keys)
137
- @remove_iteration_indent = remove_iteration_indent
210
+ @strip_iteration_indent = strip_iteration_indent
138
211
  end
139
212
 
140
213
  def assign_type(tag, iteration_tag, fallback_tag)
141
214
  node_tag_pairs = [
142
215
  [ValueNode, *tag],
143
216
  [IterationNode, *iteration_tag],
144
- [FallbackNode, *fallback_tag]
217
+ [FallbackNode, *fallback_tag],
145
218
  ]
146
219
 
147
- @head, @tail, @head_of, @tail_of = PseudoHiki.associate_nodes_with_tags(node_tag_pairs)
220
+ @head, @tail, @head_of, @tail_of = map_nodes_to_tags(node_tag_pairs)
221
+ end
222
+
223
+ def map_nodes_to_tags(node_tag_pairs)
224
+ PseudoHiki.associate_nodes_with_tags(node_tag_pairs)
148
225
  end
149
226
 
150
- register
151
- register(:square_brackets, ["[[", "]]"], ["[[#", "#]]"], ["[[*", "*]]"])
152
- register(:curly_brackets, ["{{", "}}"], ["{{#", "#}}"], ["{{*", "*}}"])
153
- register(:xml_like1, ["<!--%", "%-->"], ["<iterate>", "</iterate>"], ["<fallback>", "</fallback>"], true)
154
- register(:xml_like2, ["<fill>", "</fill>"], ["<iterate>", "</iterate>"], ["<fallback>", "</fallback>"], true)
155
- register(:xml_comment_like, ["<!--%", "%-->"], ["<!--%iterate%-->", "<!--%/iterate%-->"], ["<!--%fallback%-->", "<!--%/fallback%-->"], true)
227
+ private :map_nodes_to_tags
228
+
229
+ PREDEFINED.each {|tag_name, tags| register(tag_name, *tags) }
156
230
  end
157
231
 
158
232
  class UserDefinedTagTypeConfigError < StandardError; end
159
233
 
160
234
  def self.parse(str, tag_name=:default)
161
235
  str = remove_indents_and_newlines_if_necessary(str, tag_name)
162
- new(str, TagType[tag_name]).parse.tree
236
+ new(str, TagType[tag_name]).parse!.tree
163
237
  end
164
238
 
165
239
  def self.register_user_defined_tag_type(config_source)
166
- config = YAML.load(config_source)
167
- %w(tag_name tag iteration_tag fallback_tag).each do |item|
168
- config[item] || raise(UserDefinedTagTypeConfigError,
169
- "\"#{item}\" should be defined.")
170
- end
171
- TagType.register(registered_tag_name = config["tag_name"].to_sym,
172
- config["tag"],
173
- config["iteration_tag"],
174
- config["fallback_tag"],
175
- config["remove_indent"] || false)
240
+ config = YAML.safe_load(config_source, [Symbol])
241
+ check_validity_of_config(config)
242
+ TagType.register(registered_tag_name = config['tag_name'].to_sym,
243
+ config['tag'],
244
+ config['iteration_tag'],
245
+ config['fallback_tag'],
246
+ config['remove_indent'] || false)
176
247
  registered_tag_name
177
248
  end
178
249
 
179
250
  def self.remove_indents_and_newlines_if_necessary(str, tag_name)
180
251
  node_types = [IterationNode, FallbackNode]
181
252
  tag_type = TagType[tag_name]
182
- if TagType[tag_name].remove_iteration_indent
253
+ if TagType[tag_name].strip_iteration_indent
183
254
  str = remove_indent_before_iteration_tags(str, tag_type)
184
255
  str = remove_indent_before_fallback_tags(str, tag_type)
185
256
  end
@@ -187,20 +258,19 @@ module AdHocTemplate
187
258
  end
188
259
 
189
260
  def self.remove_indent_before_iteration_tags(template_source, tag_type)
190
- start_tag, end_tag = [
191
- tag_type.head_of[IterationNode],
192
- tag_type.tail_of[IterationNode],
193
- ].map {|tag| Regexp.escape(tag) }
194
- template_source.gsub(/^([ \t]+#{start_tag}\S*#{LINE_END_STR})/) {|s| s.lstrip }
195
- .gsub(/^([ \t]+#{end_tag}#{LINE_END_STR})/) {|s| s.lstrip }
261
+ start_tag, end_tag = regexp_escape_tag_pair(tag_type, IterationNode)
262
+ template_source.gsub(/^([ \t]+#{start_tag}\S*#{LINE_END_STR})/, &:lstrip)
263
+ .gsub(end_tag_alone_re(end_tag), &:lstrip)
196
264
  end
197
265
 
198
266
  def self.remove_indent_before_fallback_tags(template_source, tag_type)
199
- tag_re_str = [
200
- tag_type.head_of[FallbackNode],
201
- tag_type.tail_of[FallbackNode],
202
- ].map {|tag| Regexp.escape(tag) }.join('|')
203
- template_source.gsub(/^([ \t]+(?:#{tag_re_str})#{LINE_END_STR})/) {|s| s.lstrip }
267
+ tag_re_str = regexp_escape_tag_pair(tag_type, FallbackNode).join('|')
268
+ template_source.gsub(end_tag_alone_re(tag_re_str), &:lstrip)
269
+ end
270
+
271
+ def self.regexp_escape_tag_pair(tag_type, node_class)
272
+ [tag_type.head_of[node_class],
273
+ tag_type.tail_of[node_class],].map {|tag| Regexp.escape(tag) }
204
274
  end
205
275
 
206
276
  def self.remove_trailing_newline_of_end_tags(node_types, source, tag_type)
@@ -210,10 +280,24 @@ module AdHocTemplate
210
280
  end
211
281
  end
212
282
 
283
+ def self.end_tag_alone_re(tag)
284
+ /^([ \t]+(?:#{tag})#{LINE_END_STR})/
285
+ end
286
+
287
+ def self.check_validity_of_config(config)
288
+ %w[tag_name tag iteration_tag fallback_tag].each do |item|
289
+ config[item] || raise(UserDefinedTagTypeConfigError,
290
+ "\"#{item}\" should be defined.")
291
+ end
292
+ end
293
+
213
294
  private_class_method(:remove_indents_and_newlines_if_necessary,
214
295
  :remove_indent_before_iteration_tags,
215
296
  :remove_indent_before_fallback_tags,
216
- :remove_trailing_newline_of_end_tags)
297
+ :regexp_escape_tag_pair,
298
+ :remove_trailing_newline_of_end_tags,
299
+ :end_tag_alone_re,
300
+ :check_validity_of_config)
217
301
 
218
302
  def initialize(source, tag)
219
303
  @tag = tag
@@ -221,13 +305,14 @@ module AdHocTemplate
221
305
  super()
222
306
  end
223
307
 
224
- def parse
225
- while token = @tokens.shift
226
- next if @tag.tail[token] == current_node.class and self.pop
227
- next if @tag.head[token] and self.push @tag.head[token].new
228
- self.push Leaf.create(token)
308
+ def parse!
309
+ @tokens.each do |token|
310
+ next if @tag.tail[token] == current_node.class && pop
311
+ next if @tag.head[token] && push(@tag.head[token].new)
312
+ push Leaf.create(token)
229
313
  end
230
314
 
315
+ @tokens = nil
231
316
  self
232
317
  end
233
318
  end