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,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'pseudohikiparser'
4
4
 
@@ -20,6 +20,8 @@ module AdHocTemplate
20
20
  private_class_method :choose_parser
21
21
  end
22
22
 
23
- assign_format("ph") {|var, record| PseudoHikiFormatter.to_xhtml(var, record) }
23
+ assign_format('ph') do |var, record|
24
+ PseudoHikiFormatter.to_xhtml(var, record)
25
+ end
24
26
  end
25
27
  end
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'ad_hoc_template'
4
4
  require 'ad_hoc_template/utils'
@@ -10,12 +10,12 @@ module AdHocTemplate
10
10
  attr_reader :output_file, :template_encoding, :template
11
11
  attr_reader :records, :recipe
12
12
 
13
- def self.update_output_files_in_recipe(recipe, force_update=false)
14
- recipe_source = open(File.expand_path(recipe)) {|file| file.read }
13
+ def self.update_output_files_in_recipe(recipe_file, force_update=false)
14
+ recipe_source = File.open(File.expand_path(recipe_file), &:read)
15
15
  recipes = YAML.load_stream(recipe_source)
16
16
  recipes.each do |recipe|
17
17
  manager = new(recipe)
18
- if manager.modified_after_last_output? or force_update
18
+ if manager.modified_after_last_output? || force_update
19
19
  manager.update_output_file
20
20
  end
21
21
  end
@@ -60,10 +60,8 @@ module AdHocTemplate
60
60
 
61
61
  def parse_template
62
62
  template_path = File.expand_path(@recipe['template'])
63
- template_source = open(template_path,
64
- open_mode(@template_encoding)) do |file|
65
- file.read
66
- end
63
+ template_source = File.open(template_path,
64
+ open_mode(@template_encoding), &:read)
67
65
  tag_type = @recipe['tag_type'] || :default
68
66
  tag_type = tag_type.to_sym unless tag_type.kind_of? Symbol
69
67
  @template = Parser.parse(template_source, tag_type)
@@ -75,7 +73,8 @@ module AdHocTemplate
75
73
  content = AdHocTemplate::DataLoader.format(@template, @records)
76
74
  mode = @template_encoding ? "wb:#{@template_encoding}" : 'wb'
77
75
  if @output_file
78
- open(File.expand_path(@output_file), mode) {|file| file.print content }
76
+ File.open(File.expand_path(@output_file),
77
+ mode) {|file| file.print content }
79
78
  else
80
79
  STDOUT.print content
81
80
  end
@@ -117,15 +116,14 @@ module AdHocTemplate
117
116
  end
118
117
 
119
118
  def setup_main_label
120
- if data_format = @default['data_format'] and
121
- [:csv, :tsv].include? data_format
119
+ if (data_format = @default['data_format']) && csv_or_tsv?(data_format)
122
120
  @default['label'] ||= RecordReader::CSVReader::HEADER_POSITION::LEFT
123
121
  end
124
122
  end
125
123
 
126
124
  def determine_data_format!(block)
127
125
  data_format = block['data_format']
128
- if not data_format and block['data']
126
+ if !data_format && block['data']
129
127
  data_format = guess_file_format(block['data'])
130
128
  end
131
129
 
@@ -133,31 +131,25 @@ module AdHocTemplate
133
131
  end
134
132
 
135
133
  def read_file(file_name, encoding)
136
- open(File.expand_path(file_name),
137
- open_mode(encoding)) do |file|
138
- file.read
139
- end
134
+ File.open(File.expand_path(file_name),
135
+ open_mode(encoding), &:read)
140
136
  end
141
137
 
142
138
  def open_mode(encoding)
143
139
  encoding ||= Encoding.default_external.names[0]
144
- mode = "rb"
145
- return mode unless encoding and not encoding.empty?
140
+ mode = 'rb'
141
+ return mode unless encoding && !encoding.empty?
146
142
  bom = /\AUTF/i =~ encoding ? 'BOM|' : ''
147
- mode += ":#{bom}#{encoding}"
143
+ "#{mode}:#{bom}#{encoding}"
148
144
  end
149
145
 
150
146
  def prepare_data_format(block)
151
147
  data_format = block['data_format']
152
- if not data_format or data_format.empty?
153
- data_format = :default
154
- end
148
+ data_format = :default if !data_format || data_format.empty?
155
149
  data_format = data_format.to_sym
156
- return data_format unless [:csv, :tsv].include? data_format
157
- if label = block['label']
158
- label = label.sub(/\A#/, '')
159
- data_format = { data_format => label }
160
- end
150
+ return data_format unless csv_or_tsv? data_format
151
+ label = block['label']
152
+ return { data_format => label.sub(/\A#/, '') } if label
161
153
  data_format
162
154
  end
163
155
 
@@ -1,14 +1,17 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
4
  require 'json'
5
5
  require 'csv'
6
6
 
7
+ require 'ad_hoc_template/shim'
8
+
7
9
  module AdHocTemplate
8
10
  module RecordReader
9
11
  module YAMLReader
10
12
  def self.read_record(yaml_data)
11
- RecordReader.convert_values_to_string(YAML.load(yaml_data))
13
+ RecordReader.convert_values_to_string(YAML.safe_load(yaml_data,
14
+ [Symbol]))
12
15
  end
13
16
 
14
17
  def self.dump(config_data)
@@ -29,10 +32,12 @@ module AdHocTemplate
29
32
  end
30
33
 
31
34
  module CSVReader
35
+ using Shim unless ''.respond_to?(:+@)
36
+
32
37
  COL_SEP = {
33
38
  csv: CSV::DEFAULT_OPTIONS[:col_sep],
34
- tsv: "\t"
35
- }
39
+ tsv: "\t",
40
+ }.freeze
36
41
 
37
42
  module HEADER_POSITION
38
43
  TOP = '__header_top__'
@@ -42,10 +47,10 @@ module AdHocTemplate
42
47
  class NotSupportedError < StandardError; end
43
48
 
44
49
  def self.read_record(csv_data, config={ csv: nil })
45
- label, sep = parse_config(config)
50
+ label, sep = parse_config(config)
46
51
  header, *data = csv_to_array(csv_data, sep, label)
47
52
  csv_records = data.map {|row| convert_to_hash(header, row) }
48
- if label and label.index('|')
53
+ if label && label.index('|')
49
54
  return compose_inner_iteration_records(csv_records, label)
50
55
  end
51
56
  compose_record(csv_records, label)
@@ -55,20 +60,15 @@ module AdHocTemplate
55
60
  data = RecordReader.parse_if_necessary(config_data)
56
61
  raise NotSupportedError unless csv_compatible_format?(data)
57
62
 
58
- if kv_pairs = find_sub_records(data)
59
- records = hashes_to_arrays(kv_pairs)
60
- else
61
- records = data.to_a.transpose
62
- end
63
+ kv_pairs = find_sub_records(data)
64
+ records = kv_pairs ? hashes_to_arrays(kv_pairs) : data.to_a.transpose
63
65
 
64
66
  array_to_csv(records, col_sep)
65
67
  end
66
68
 
67
69
  def self.convert_to_hash(header, row_array)
68
70
  {}.tap do |record|
69
- header.zip(row_array).each do |key, value|
70
- record[key] = value
71
- end
71
+ header.zip(row_array).each {|key, value| record[key] = value }
72
72
  end
73
73
  # if RUBY_VERSION >= 2.1.0: header.zip(row_array).to_h
74
74
  end
@@ -82,15 +82,13 @@ module AdHocTemplate
82
82
  when Hash
83
83
  format, label = config.to_a[0]
84
84
  end
85
- col_sep = COL_SEP[format||:csv]
86
- return label, col_sep
85
+ col_sep = COL_SEP[format || :csv]
86
+ [label, col_sep]
87
87
  end
88
88
 
89
89
  def self.csv_to_array(csv_data, col_sep, label)
90
90
  array = CSV.new(csv_data, col_sep: col_sep).to_a
91
- if not label or label == HEADER_POSITION::LEFT
92
- array = array.transpose
93
- end
91
+ array = array.transpose if !label || label == HEADER_POSITION::LEFT
94
92
  array
95
93
  end
96
94
 
@@ -123,15 +121,15 @@ module AdHocTemplate
123
121
  end
124
122
 
125
123
  def self.inner_iteration_labels(outer_label, inner_label, keys)
126
- labels = keys.inject({}) do |h, key|
127
- h[key] = [outer_label, inner_label, key].join('|')
128
- h
124
+ keys.each_with_object({}) do |key, labels|
125
+ labels[key] = [outer_label, inner_label, key].join('|')
129
126
  end
130
127
  end
131
128
 
132
129
  def self.csv_compatible_format?(data)
133
130
  iteration_blocks_count = data.values.count {|v| v.kind_of? Array }
134
- iteration_blocks_count == 0 or (iteration_blocks_count == 1 && data.size == 1)
131
+ iteration_blocks_count.zero? ||
132
+ (iteration_blocks_count == 1 && data.size == 1)
135
133
  end
136
134
 
137
135
  def self.hashes_to_arrays(data)
@@ -147,10 +145,11 @@ module AdHocTemplate
147
145
  def self.array_to_csv(records, col_sep)
148
146
  # I do not adopt "records.map {|rec| rec.to_csv }.join",
149
147
  # because I'm not sure if it is sufficient for certain data or not.
150
- # For example, a field value may contain carriage returns or line feeds,
151
- # and in that case, improper handling of the end of record would be damaging.
148
+ # For example, a field value may contain carriage returns or line
149
+ # feeds, and in that case, improper handling of the end of record
150
+ # would be damaging.
152
151
 
153
- CSV.generate('', col_sep: col_sep) do |csv|
152
+ CSV.generate(+'', col_sep: col_sep) do |csv|
154
153
  records.each {|record| csv << record }
155
154
  end
156
155
  end
@@ -179,17 +178,17 @@ module AdHocTemplate
179
178
 
180
179
  module DefaultFormReader
181
180
  SEPARATOR = /:\s*/o
182
- BLOCK_HEAD = /\A\/\/\/@/o
183
- ITERATION_HEAD = /\A\/\/\/@#/o
181
+ BLOCK_HEAD = %r{\A///@}
182
+ ITERATION_HEAD = %r{\A///@#}
184
183
  EMPTY_LINE = /\A#{LINE_END_STR}\Z/o
185
184
  ITERATION_MARK = /\A#/o
186
- COMMENT_HEAD = /\A\/\/\/\//
185
+ COMMENT_HEAD = %r{\A////}
187
186
  READERS_RE = {
188
187
  key_value: SEPARATOR,
189
188
  iteration: ITERATION_HEAD,
190
189
  block: BLOCK_HEAD,
191
190
  empty_line: EMPTY_LINE,
192
- }
191
+ }.freeze
193
192
 
194
193
  class ReaderState
195
194
  attr_accessor :current_block_label
@@ -266,6 +265,8 @@ module AdHocTemplate
266
265
  end
267
266
 
268
267
  class Reader
268
+ using Shim unless //.respond_to? :match?
269
+
269
270
  def self.setup_reader(stack)
270
271
  readers = {}
271
272
  {
@@ -273,9 +274,7 @@ module AdHocTemplate
273
274
  key_value: KeyValueReader,
274
275
  block: BlockReader,
275
276
  iteration: IterationReader,
276
- }.each do |k, v|
277
- readers[k] = v.new(stack, readers)
278
- end
277
+ }.each {|k, v| readers[k] = v.new(stack, readers) }
279
278
  stack.push readers[:base]
280
279
  readers
281
280
  end
@@ -289,28 +288,28 @@ module AdHocTemplate
289
288
  @stack.pop
290
289
  end
291
290
 
292
- def read(line)
293
- end
291
+ def read(line); end
294
292
 
295
293
  private
296
294
 
297
295
  def push_reader_if_match(line, readers)
298
296
  readers.each do |reader|
299
- return @stack.push(@readers[reader]) if READERS_RE[reader] === line
297
+ if READERS_RE[reader].match?(line)
298
+ return @stack.push(@readers[reader])
299
+ end
300
300
  end
301
301
  end
302
302
 
303
303
  def setup_new_block(line, initial_value)
304
- label = line.sub(BLOCK_HEAD, "").chomp
304
+ label = line.sub(BLOCK_HEAD, '').chomp
305
305
  @stack.current_record[label] ||= initial_value
306
306
  @stack.current_block_label = label
307
307
  end
308
308
  end
309
309
 
310
-
311
310
  class BaseReader < Reader
312
311
  def setup_stack(line)
313
- push_reader_if_match(line, [:iteration, :block, :key_value])
312
+ push_reader_if_match(line, %i[iteration block key_value])
314
313
  end
315
314
  end
316
315
 
@@ -320,7 +319,7 @@ module AdHocTemplate
320
319
  when EMPTY_LINE, ITERATION_HEAD, BLOCK_HEAD
321
320
  pop_stack
322
321
  end
323
- push_reader_if_match(line, [:iteration, :block])
322
+ push_reader_if_match(line, %i[iteration block])
324
323
  end
325
324
 
326
325
  def read(line)
@@ -331,20 +330,22 @@ module AdHocTemplate
331
330
  end
332
331
 
333
332
  class BlockReader < Reader
333
+ using Shim unless ''.respond_to?(:+@)
334
+
334
335
  def setup_stack(line)
335
336
  case line
336
337
  when ITERATION_HEAD, BLOCK_HEAD
337
338
  @stack.remove_trailing_empty_lines_from_last_block!
338
339
  pop_stack
339
340
  end
340
- push_reader_if_match(line, [:iteration, :block])
341
+ push_reader_if_match(line, %i[iteration block])
341
342
  end
342
343
 
343
344
  def read(line)
344
345
  block_value = @stack.last_block_value
345
346
  case line
346
347
  when BLOCK_HEAD
347
- setup_new_block(line, String.new)
348
+ setup_new_block(line, +'')
348
349
  when EMPTY_LINE, COMMENT_HEAD
349
350
  block_value << line unless block_value.empty?
350
351
  else
@@ -389,7 +390,12 @@ module AdHocTemplate
389
390
  iteration_part = format_iteration_block(iteration_keys, labels)
390
391
  block_part = format_key_value_block(block_keys, labels)
391
392
 
392
- [key_value_part, iteration_part, block_part].join($/).sub(/(#{$/}+)\Z/, $/)
393
+ all_parts = [key_value_part, iteration_part, block_part].join($/)
394
+ remove_redundant_newlines(all_parts)
395
+ end
396
+
397
+ def self.remove_redundant_newlines(str)
398
+ str.sub(/(#{$/}+)\Z/, $/)
393
399
  end
394
400
 
395
401
  def self.format_key_value_pairs(key_names, labels={})
@@ -399,33 +405,32 @@ module AdHocTemplate
399
405
  def self.format_key_value_block(key_names, labels)
400
406
  [].tap do |blocks|
401
407
  key_names.each do |key|
402
- blocks.push "///@#{key}#{$/*2}#{labels[key]}"
408
+ blocks.push "///@#{key}#{$/ * 2}#{labels[key]}"
403
409
  end
404
410
  end.join($/)
405
411
  end
406
412
 
407
413
  def self.format_iteration_block(key_names, labels)
408
414
  key_names.map do |iteration_label|
409
- iteration_block = ["///@#{iteration_label}#{$/}"]
410
- labels[iteration_label].each do |sub_record|
411
- iteration_block.push format_key_value_pairs(sub_record.keys, sub_record)
415
+ iteration_block = labels[iteration_label].map do |sub_record|
416
+ format_key_value_pairs(sub_record.keys, sub_record)
412
417
  end
413
- iteration_block.join($/)
418
+ iteration_block.unshift("///@#{iteration_label}#{$/}")
414
419
  end.join($/)
415
420
  end
416
421
 
417
422
  def self.categorize_keys(labels)
418
- iteration_part, rest = labels.partition do |e|
419
- e[1].kind_of? Array
420
- end.map {|e| e.map(&:first) }
423
+ iteration_part, rest = labels.partition {|e| e[1].kind_of? Array }
424
+ .map {|e| e.map(&:first) }
421
425
 
422
426
  block_part, key_value_part = rest.partition do |e|
423
427
  LINE_END_RE =~ labels[e]
424
428
  end
425
429
 
426
- return iteration_part, key_value_part, block_part
430
+ [iteration_part, key_value_part, block_part]
427
431
  end
428
432
 
433
+ private_class_method :remove_redundant_newlines
429
434
  private_class_method :format_key_value_pairs
430
435
  private_class_method :format_key_value_block
431
436
  private_class_method :format_iteration_block
@@ -438,9 +443,7 @@ module AdHocTemplate
438
443
  csv: CSVReader,
439
444
  tsv: TSVReader,
440
445
  default: DefaultFormReader,
441
- }
442
-
443
- FORMAT_NAME_TO_READER.default = DefaultFormReader
446
+ }.tap {|h| h.default = DefaultFormReader }.freeze
444
447
 
445
448
  def self.dump(data_source, target_format=:default)
446
449
  FORMAT_NAME_TO_READER[target_format].dump(data_source)
@@ -463,7 +466,7 @@ module AdHocTemplate
463
466
  data.each do |k, v|
464
467
  if v.kind_of? Array
465
468
  v.each {|sub_rec| convert_values_to_string(sub_rec) }
466
- elsif v and not v.kind_of? String
469
+ elsif v && !v.kind_of?(String)
467
470
  data[k] = v.to_s
468
471
  end
469
472
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdHocTemplate
4
+ ##
5
+ # Provide methods that are not availabe in older versions of Ruby.
6
+
7
+ module Shim
8
+ refine Regexp do
9
+ ##
10
+ # Regexp.match?() is available for Ruby >= 2.4,
11
+ # and the following implementation does not satisfy
12
+ # the full specification of the original method.
13
+
14
+ alias_method(:match?, :===)
15
+ end
16
+
17
+ refine String do
18
+ ##
19
+ # Regexp.match?() is available for Ruby >= 2.3,
20
+ # and the following implementation does not satisfy
21
+ # the full specification of the original method.
22
+
23
+ alias_method(:+@, :dup)
24
+ end
25
+ end
26
+ end
@@ -1,16 +1,16 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  module AdHocTemplate
4
4
  module Utils
5
- FILE_EXTENTIONS = {
5
+ FILE_EXTENTIONS = {
6
6
  /\.ya?ml\Z/i => :yaml,
7
7
  /\.json\Z/i => :json,
8
8
  /\.csv\Z/i => :csv,
9
9
  /\.tsv\Z/i => :tsv,
10
- }
10
+ }.freeze
11
11
 
12
12
  def guess_file_format(filename)
13
- if_any_regex_match(FILE_EXTENTIONS, filename) do |ext_re, format|
13
+ if_any_regex_match(FILE_EXTENTIONS, filename) do |_, format|
14
14
  return format
15
15
  end
16
16
  end
@@ -19,11 +19,17 @@ module AdHocTemplate
19
19
  regex_table.each do |re, paired_value|
20
20
  if re =~ target
21
21
  yield re, paired_value
22
- return
22
+ return nil
23
23
  end
24
24
  end
25
25
  STDERR.puts failure_message if failure_message
26
26
  nil
27
27
  end
28
+
29
+ private
30
+
31
+ def csv_or_tsv?(format)
32
+ %i[csv tsv].include? format
33
+ end
28
34
  end
29
35
  end