ad_hoc_template 0.4.0 → 0.4.1

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.
@@ -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