ad_hoc_template 0.3.0 → 0.4.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/ad_hoc_template.gemspec +1 -1
- data/lib/ad_hoc_template/command_line_interface.rb +53 -26
- data/lib/ad_hoc_template/config_manager.rb +129 -0
- data/lib/ad_hoc_template/default_tag_formatter.rb +6 -1
- data/lib/ad_hoc_template/entry_format_generator.rb +83 -12
- data/lib/ad_hoc_template/parser.rb +64 -59
- data/lib/ad_hoc_template/recipe_manager.rb +168 -0
- data/lib/ad_hoc_template/record_reader.rb +73 -16
- data/lib/ad_hoc_template/utils.rb +29 -0
- data/lib/ad_hoc_template/version.rb +1 -1
- data/lib/ad_hoc_template.rb +77 -25
- data/samples/en/inner_iteration/data.aht +21 -0
- data/samples/en/inner_iteration/data.csv +5 -0
- data/samples/en/inner_iteration/data.yaml +14 -0
- data/samples/en/inner_iteration/data2.yaml +14 -0
- data/samples/en/inner_iteration/for_csv.sh +3 -0
- data/samples/en/inner_iteration/template.html +18 -0
- data/samples/en/recipe/main.aht +9 -0
- data/samples/en/recipe/template.html +20 -0
- data/samples/for_recipe.sh +3 -0
- data/samples/ja/inner_iteration/data.aht +21 -0
- data/samples/ja/inner_iteration/data.csv +5 -0
- data/samples/ja/inner_iteration/data.yaml +14 -0
- data/samples/ja/inner_iteration/data2.yaml +14 -0
- data/samples/ja/inner_iteration/for_csv.sh +3 -0
- data/samples/ja/inner_iteration/template.html +18 -0
- data/samples/ja/recipe/main.aht +9 -0
- data/samples/ja/recipe/template.html +20 -0
- data/samples/recipe.yaml +34 -0
- data/spec/ad_hoc_template_spec.rb +71 -1
- data/spec/command_line_interface_spec.rb +105 -11
- data/spec/config_manager_spec.rb +142 -0
- data/spec/default_tag_formatter_spec.rb +13 -0
- data/spec/entry_format_generator_spec.rb +160 -17
- data/spec/parser_spec.rb +64 -20
- data/spec/recipe_manager_spec.rb +419 -0
- data/spec/record_reader_spec.rb +122 -1
- data/spec/test_data/en/inner_iteration/data.csv +5 -0
- data/spec/test_data/en/recipe/expected_result.html +32 -0
- data/spec/test_data/en/recipe/main.aht +9 -0
- data/spec/test_data/en/recipe/template.html +20 -0
- data/spec/test_data/ja/inner_iteration/data.csv +5 -0
- data/spec/test_data/ja/recipe/expected_result.html +32 -0
- data/spec/test_data/ja/recipe/main.aht +9 -0
- data/spec/test_data/ja/recipe/template.html +20 -0
- data/spec/test_data/recipe.yaml +34 -0
- metadata +47 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddd8ddbfdc477efb70980b40c3e16848108df9ea
|
4
|
+
data.tar.gz: fb506b375a6781451f00f32c6a88b58a2fab210c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5e5ef55188915bd037bf8b20e67b5a3226f54aa1037345510dc5a10319f4cf59711a7627c35c52c716f5633abe3170b9b6f09b56c04ec5b71a90b47c56746d9
|
7
|
+
data.tar.gz: 9246fcb22fabbab24a8866ef612474121d5acc42cb4fa65a333e717aac26cc4dc7a4ebdd9d708c99eb0cdf8294fc207d98c0a43a5c69e2a2459650700dd0598d
|
data/ad_hoc_template.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
-
spec.add_runtime_dependency "pseudohikiparser", "0.0.
|
20
|
+
spec.add_runtime_dependency "pseudohikiparser", "0.0.6.develop"
|
21
21
|
spec.add_runtime_dependency "optparse_plus"
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
@@ -2,9 +2,15 @@
|
|
2
2
|
|
3
3
|
require 'ad_hoc_template'
|
4
4
|
require 'optparse_plus'
|
5
|
+
require 'ad_hoc_template/config_manager'
|
6
|
+
require 'ad_hoc_template/recipe_manager'
|
7
|
+
require 'ad_hoc_template/utils'
|
8
|
+
|
9
|
+
AdHocTemplate::ConfigManager.require_local_settings
|
5
10
|
|
6
11
|
module AdHocTemplate
|
7
12
|
class CommandLineInterface
|
13
|
+
include Utils
|
8
14
|
attr_accessor :output_filename, :template_data, :record_data, :tag_type, :data_format
|
9
15
|
attr_writer :output_empty_entry
|
10
16
|
|
@@ -25,18 +31,12 @@ module AdHocTemplate
|
|
25
31
|
/\At(sv)?/i => :tsv,
|
26
32
|
}
|
27
33
|
|
28
|
-
FILE_EXTENTIONS = {
|
29
|
-
/\.ya?ml\Z/i => :yaml,
|
30
|
-
/\.json\Z/i => :json,
|
31
|
-
/\.csv\Z/i => :csv,
|
32
|
-
/\.tsv\Z/i => :tsv,
|
33
|
-
}
|
34
|
-
|
35
34
|
def initialize
|
36
35
|
@tag_formatter = AdHocTemplate::DefaultTagFormatter.new
|
37
36
|
@output_filename = nil
|
38
37
|
@tag_type = :default
|
39
38
|
@data_format = nil
|
39
|
+
@force_update = false
|
40
40
|
end
|
41
41
|
|
42
42
|
def parse_command_line_options
|
@@ -50,6 +50,10 @@ module AdHocTemplate
|
|
50
50
|
opt.on(:data_format) {|data_format| choose_data_format(data_format) }
|
51
51
|
opt.on(:tag_config) {|tag_config_yaml| register_user_defined_tag_type(tag_config_yaml) }
|
52
52
|
opt.on(:entry_format) {|entry_format| @output_empty_entry = true }
|
53
|
+
opt.on(:init_local_settings) { init_local_settings }
|
54
|
+
opt.on(:recipe_template) { @output_recipe_template = true }
|
55
|
+
opt.on(:cooking_recipe) {|recipe_yaml| @recipe_yaml = recipe_yaml }
|
56
|
+
opt.on(:force_update) { @force_update = true }
|
53
57
|
|
54
58
|
opt.parse!
|
55
59
|
end
|
@@ -78,7 +82,26 @@ module AdHocTemplate
|
|
78
82
|
|
79
83
|
def generate_entry_format
|
80
84
|
tree = Parser.parse(@template_data, @tag_type)
|
81
|
-
EntryFormatGenerator.
|
85
|
+
EntryFormatGenerator.extract_form(tree, @data_format)
|
86
|
+
end
|
87
|
+
|
88
|
+
def init_local_settings
|
89
|
+
AdHocTemplate::ConfigManager.init_local_settings
|
90
|
+
config_dir = File.expand_path(AdHocTemplate::ConfigManager::LOCAL_SETTINGS_DIR)
|
91
|
+
puts "Please edit configuration files created in #{config_dir}"
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
|
95
|
+
def generate_recipe_template(templates)
|
96
|
+
encoding = Encoding.default_external.names[0]
|
97
|
+
AdHocTemplate::EntryFormatGenerator.
|
98
|
+
extract_recipes_from_template_files(templates,
|
99
|
+
@tag_type)
|
100
|
+
end
|
101
|
+
|
102
|
+
def update_output_files_in_recipe(recipe)
|
103
|
+
AdHocTemplate::RecipeManager.update_output_files_in_recipe(recipe,
|
104
|
+
@force_update)
|
82
105
|
end
|
83
106
|
|
84
107
|
def open_output
|
@@ -93,8 +116,15 @@ module AdHocTemplate
|
|
93
116
|
|
94
117
|
def execute
|
95
118
|
parse_command_line_options
|
119
|
+
return update_output_files_in_recipe(@recipe_yaml) if @recipe_yaml
|
96
120
|
read_input_files
|
97
|
-
output = @output_empty_entry
|
121
|
+
output = if @output_empty_entry
|
122
|
+
generate_entry_format
|
123
|
+
elsif @output_recipe_template
|
124
|
+
generate_recipe_template(ARGV)
|
125
|
+
else
|
126
|
+
render
|
127
|
+
end
|
98
128
|
open_output {|out| out.print output }
|
99
129
|
end
|
100
130
|
|
@@ -126,23 +156,6 @@ module AdHocTemplate
|
|
126
156
|
return format if iteration_label.nil? or iteration_label.empty?
|
127
157
|
{ format => iteration_label }
|
128
158
|
end
|
129
|
-
|
130
|
-
def guess_file_format(filename)
|
131
|
-
if_any_regex_match(FILE_EXTENTIONS, filename) do |ext_re, format|
|
132
|
-
return format
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def if_any_regex_match(regex_table, target, failure_message=nil)
|
137
|
-
regex_table.each do |re, paired_value|
|
138
|
-
if re =~ target
|
139
|
-
yield re, paired_value
|
140
|
-
return
|
141
|
-
end
|
142
|
-
end
|
143
|
-
STDERR.puts failure_message if failure_message
|
144
|
-
nil
|
145
|
-
end
|
146
159
|
end
|
147
160
|
end
|
148
161
|
|
@@ -167,3 +180,17 @@ entry_format:
|
|
167
180
|
short: "-e"
|
168
181
|
long: "--entry-format"
|
169
182
|
description: "Extract tag labels from a template and generate an empty data entry format"
|
183
|
+
init_local_settings:
|
184
|
+
long: "--init-local-settings"
|
185
|
+
description: "Generate configuration files for local settings"
|
186
|
+
recipe_template:
|
187
|
+
short: "-R"
|
188
|
+
long: "--recipe-template"
|
189
|
+
description: "Generate recipe entries for template files given on the command line"
|
190
|
+
cooking_recipe:
|
191
|
+
short: "-c [recipe_yaml]"
|
192
|
+
long: "--cooking-recipe [=recipe_yaml]"
|
193
|
+
description: "Update output files specified in the recipe file"
|
194
|
+
force_update:
|
195
|
+
long: "--force-update"
|
196
|
+
description: "Update output files in recipe, even when they are newer than template/data files"
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module AdHocTemplate
|
6
|
+
class ConfigManager
|
7
|
+
LOCAL_SETTINGS_DIR = '~/.ad_hoc_template/'
|
8
|
+
SETTINGS_FILE_NAME = 'settings.rb'
|
9
|
+
TAG_DEF_FILE_NAME = 'user_defined_tag.yaml'
|
10
|
+
LOCAL_SETTINGS_FILE = File.join(LOCAL_SETTINGS_DIR,
|
11
|
+
SETTINGS_FILE_NAME)
|
12
|
+
|
13
|
+
def self.require_local_settings
|
14
|
+
settings_file = File.expand_path(LOCAL_SETTINGS_FILE)
|
15
|
+
if File.exist? settings_file
|
16
|
+
require settings_file
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.configure(&config_block)
|
21
|
+
module_eval(&config_block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.user_defined_tag(def_yaml_path)
|
25
|
+
yaml_source = File.read(expand_path(def_yaml_path))
|
26
|
+
AdHocTemplate::Parser.register_user_defined_tag_type(yaml_source)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.assign_format_label(format_label, &func)
|
30
|
+
AdHocTemplate::DefaultTagFormatter.assign_format(format_label, &func)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.define_label_format(&block)
|
34
|
+
AdHocTemplate::DefaultTagFormatter.module_eval(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.init_local_settings
|
38
|
+
config_dir = File.expand_path(LOCAL_SETTINGS_DIR)
|
39
|
+
settings_rb = File.expand_path(LOCAL_SETTINGS_FILE)
|
40
|
+
custom_tag_yaml = File.join(config_dir, TAG_DEF_FILE_NAME)
|
41
|
+
FileUtils.mkdir(config_dir) unless File.exist? config_dir
|
42
|
+
create_unless_exist(settings_rb, @local_settings_template)
|
43
|
+
create_unless_exist(custom_tag_yaml, @custom_tag_template)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.expand_path(path)
|
47
|
+
unless /\A[\.\/]/ =~ path
|
48
|
+
path = File.join(LOCAL_SETTINGS_DIR, path)
|
49
|
+
end
|
50
|
+
File.expand_path(path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.create_unless_exist(path, content)
|
54
|
+
return if File.exist? path
|
55
|
+
open(path, "w") {|file| file.print content }
|
56
|
+
end
|
57
|
+
|
58
|
+
private_class_method :expand_path
|
59
|
+
private_class_method :create_unless_exist
|
60
|
+
|
61
|
+
@local_settings_template = <<SETTING_TEMPLATE
|
62
|
+
AdHocTemplate.local_settings do
|
63
|
+
##
|
64
|
+
# If you want to define your own tags for templates,
|
65
|
+
# prepare a definition file in YAML format:
|
66
|
+
#
|
67
|
+
# An example of definition in YAML -----
|
68
|
+
# ---
|
69
|
+
# tag_name: :default
|
70
|
+
# tag: ["<%", "%>"]
|
71
|
+
# iteration_tag: ["<%#", "#%>"]
|
72
|
+
# fallback_tag: ["<%*", "*%>"]
|
73
|
+
# remove_indent: false
|
74
|
+
#
|
75
|
+
# And read the file by "user_defined_tag NAME_OF_YAML_FILE":
|
76
|
+
#
|
77
|
+
user_defined_tag 'user_defined_tag.yaml'
|
78
|
+
|
79
|
+
##
|
80
|
+
# If you want to define a custom label format,
|
81
|
+
# there are two ways to realize that.
|
82
|
+
# Suppose you have data in YAML and want to
|
83
|
+
# present the value of date field in French style:
|
84
|
+
#
|
85
|
+
# YAML data -----
|
86
|
+
# ---
|
87
|
+
# date: 2016/09/28
|
88
|
+
# other_field: ...
|
89
|
+
# ...
|
90
|
+
#
|
91
|
+
# Template -----
|
92
|
+
#
|
93
|
+
# Today = <%= date %>
|
94
|
+
# Aujourd'hui = <%fd date %>
|
95
|
+
#
|
96
|
+
# Expected result -----
|
97
|
+
#
|
98
|
+
# Today = 2016/09/28
|
99
|
+
# Aujourd'hui = 28/09/2016
|
100
|
+
#
|
101
|
+
# # The first way -----
|
102
|
+
#
|
103
|
+
# define_label_format do
|
104
|
+
# def french_date(var, record)
|
105
|
+
# record[var].split(/\\//).reverse.join('/')
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# assign_format french_date: 'fd'
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# # The second way -----
|
112
|
+
#
|
113
|
+
# assign_format_label('fd') do |var, record|
|
114
|
+
# record[var].split(/\\//).reverse.join('/')
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
end
|
118
|
+
SETTING_TEMPLATE
|
119
|
+
|
120
|
+
@custom_tag_template =<<TAG_TEMPLATE
|
121
|
+
---
|
122
|
+
tag_name: :default
|
123
|
+
tag: ["<%", "%>"]
|
124
|
+
iteration_tag: ["<%#", "#%>"]
|
125
|
+
fallback_tag: ["<%*", "*%>"]
|
126
|
+
remove_indent: false
|
127
|
+
TAG_TEMPLATE
|
128
|
+
end
|
129
|
+
end
|
@@ -8,7 +8,12 @@ module AdHocTemplate
|
|
8
8
|
}
|
9
9
|
|
10
10
|
def self.assign_format(format_label, &func)
|
11
|
-
|
11
|
+
if format_label.kind_of? Hash and not func
|
12
|
+
func_name, label = format_label.to_a.flatten
|
13
|
+
FUNCTION_TABLE[label] = func_name
|
14
|
+
else
|
15
|
+
FUNCTION_TABLE[format_label] = func
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
def find_function(format_label)
|
@@ -8,42 +8,113 @@ module AdHocTemplate
|
|
8
8
|
@labels = {}
|
9
9
|
end
|
10
10
|
|
11
|
-
def visit(tree)
|
11
|
+
def visit(tree, memo)
|
12
12
|
case tree
|
13
|
-
when Parser::
|
14
|
-
visit_iteration_tag_node(tree)
|
13
|
+
when Parser::IterationNode, Parser::FallbackNode
|
14
|
+
visit_iteration_tag_node(tree, memo)
|
15
15
|
when Parser::TagNode
|
16
16
|
@labels[tree.join.strip] = nil
|
17
17
|
when Parser::Node
|
18
|
-
tree.each {|node| node.accept(self) }
|
18
|
+
tree.each {|node| node.accept(self, memo) }
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
-
def visit_iteration_tag_node(tree)
|
24
|
+
def visit_iteration_tag_node(tree, memo)
|
25
25
|
if iteration_label = tree.type
|
26
26
|
sub_checker = self.class.new
|
27
27
|
@labels[iteration_label] = [sub_checker.labels]
|
28
|
-
tree.each { |node| node.accept(sub_checker) }
|
28
|
+
tree.each { |node| node.accept(sub_checker, memo) }
|
29
29
|
else
|
30
|
-
tree.each {|node| node.accept(self) }
|
30
|
+
tree.each {|node| node.accept(self, memo) }
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.
|
36
|
-
labels =
|
35
|
+
def self.extract_form(parsed_template, target_format=nil, memo=nil)
|
36
|
+
labels = extract_form_as_ruby_objects(parsed_template, memo)
|
37
|
+
labels = pull_up_inner_iterations(labels)
|
37
38
|
|
38
39
|
RecordReader.dump(labels, target_format)
|
39
40
|
end
|
40
41
|
|
41
|
-
def self.
|
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)
|
48
|
+
end
|
49
|
+
|
50
|
+
recipes.join
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.extract_recipe(template_source, template_path,
|
54
|
+
tag_type=:default, encoding='UTF-8')
|
55
|
+
recipe = recipe_entry(template_path, tag_type, encoding)
|
56
|
+
parsed_template = Parser.parse(template_source, tag_type)
|
57
|
+
extract_iteration_labels(parsed_template).each do |label|
|
58
|
+
recipe['blocks'].push recipe_block_entry(label) if label.start_with? '#'
|
59
|
+
end
|
60
|
+
|
61
|
+
RecordReader.dump(recipe, :yaml)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.extract_iteration_labels(parsed_template, memo=nil)
|
65
|
+
labels = extract_form_as_ruby_objects(parsed_template, memo)
|
66
|
+
pull_up_inner_iterations(labels).keys
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.extract_form_as_ruby_objects(parsed_template, memo)
|
42
70
|
label_checker = LabelChecker.new
|
43
|
-
parsed_template.accept(label_checker)
|
71
|
+
parsed_template.accept(label_checker, memo)
|
44
72
|
label_checker.labels
|
45
73
|
end
|
46
74
|
|
47
|
-
|
75
|
+
def self.pull_up_inner_iterations(labels)
|
76
|
+
each_iteration_label(labels) do |label|
|
77
|
+
labels[label].each do |record|
|
78
|
+
each_iteration_label(record) do |key|
|
79
|
+
inner_label = [label, key.sub(/\A#/, '')].join('|')
|
80
|
+
labels[inner_label] = record.delete(key)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
labels
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.each_iteration_label(labels)
|
88
|
+
labels.keys.each do |label|
|
89
|
+
yield label if labels[label].kind_of? Array
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.recipe_entry(template_path, tag_type, encoding)
|
94
|
+
recipe = {
|
95
|
+
'template' => template_path,
|
96
|
+
'tag_type' => tag_type,
|
97
|
+
'template_encoding' => encoding,
|
98
|
+
'data' => nil,
|
99
|
+
'data_format' => nil,
|
100
|
+
'data_encoding' => nil,
|
101
|
+
'output_file' => nil,
|
102
|
+
'blocks' => []
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.recipe_block_entry(label)
|
107
|
+
{
|
108
|
+
'label' => label,
|
109
|
+
'data' => nil,
|
110
|
+
'data_format' => nil,
|
111
|
+
'data_encoding' => nil,
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
private_class_method :extract_form_as_ruby_objects
|
116
|
+
private_class_method :pull_up_inner_iterations
|
117
|
+
private_class_method :each_iteration_label
|
118
|
+
private_class_method :recipe_entry, :recipe_block_entry
|
48
119
|
end
|
49
120
|
end
|
@@ -18,31 +18,35 @@ module AdHocTemplate
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def contains_any_value_assigned_tag_node?(record)
|
21
|
-
|
22
|
-
if node.
|
23
|
-
return true if any_value_assigned_to_iteration_tag?(node, record)
|
24
|
-
else
|
25
|
-
val = record[node.join.strip]
|
26
|
-
return true if val and not val.empty?
|
27
|
-
end
|
21
|
+
each_tag_node do |node|
|
22
|
+
return true if node.contains_any_value_assigned_tag_node?(record)
|
28
23
|
end
|
29
|
-
false
|
30
24
|
end
|
31
25
|
|
32
26
|
def contains_any_value_tag?
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
each_tag_node {|node| return true if node.contains_any_value_tag? }
|
28
|
+
end
|
29
|
+
|
30
|
+
def inner_iteration_tag_labels
|
31
|
+
names = []
|
32
|
+
each_tag_node do |node|
|
33
|
+
next unless node.kind_of? IterationNode
|
34
|
+
names.push node.type if node.type
|
35
|
+
if inner_names = node.inner_iteration_tag_labels
|
36
|
+
names.concat inner_names
|
39
37
|
end
|
40
38
|
end
|
41
|
-
|
39
|
+
|
40
|
+
names unless names.empty?
|
42
41
|
end
|
43
42
|
|
44
43
|
private
|
45
44
|
|
45
|
+
def each_tag_node
|
46
|
+
each {|node| yield node if node.kind_of?(TagNode) }
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
46
50
|
def assign_value_to_type(first_leaf)
|
47
51
|
if first_leaf.kind_of? String and /\A\s/ =~ first_leaf
|
48
52
|
return first_leaf.sub(/\A#{LINE_END_STR}/, "")
|
@@ -55,45 +59,60 @@ module AdHocTemplate
|
|
55
59
|
sep = /\A\S*#{LINE_END_STR}/ =~ first_leaf ? LINE_END_RE : /\s+/
|
56
60
|
first_leaf.split(sep, 2)
|
57
61
|
end
|
58
|
-
|
59
|
-
def empty_sub_records?(record, node)
|
60
|
-
sub_records = record[node.type]
|
61
|
-
return true if sub_records.nil? or sub_records.empty?
|
62
|
-
sub_records.each do |rec|
|
63
|
-
return false if rec.values.find {|val| val and not val.empty? }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def any_value_assigned_to_iteration_tag?(tag_node, record)
|
68
|
-
if tag_node.type
|
69
|
-
not empty_sub_records?(record, tag_node)
|
70
|
-
elsif tag_node.kind_of? FallbackTagNode
|
71
|
-
false
|
72
|
-
else
|
73
|
-
tag_node.contains_any_value_assigned_tag_node?(record)
|
74
|
-
end
|
75
|
-
end
|
76
62
|
end
|
77
63
|
|
78
|
-
class
|
64
|
+
class IterationNode < TagNode
|
79
65
|
def assign_value_to_type(first_leaf)
|
80
66
|
return first_leaf unless first_leaf.kind_of? String
|
81
67
|
|
82
68
|
if /\A[^\s:]*:\s/ =~ first_leaf
|
83
|
-
@type, remaining_part = first_leaf.split(
|
69
|
+
@type, remaining_part = first_leaf.split(/:(?:#{LINE_END_STR}|\s)/, 2)
|
84
70
|
@type = @type.empty? ? nil : '#'.freeze + @type
|
85
71
|
return remaining_part
|
86
72
|
end
|
87
73
|
|
88
74
|
first_leaf.sub(/\A#{LINE_END_STR}/, '')
|
89
75
|
end
|
76
|
+
|
77
|
+
def contains_any_value_assigned_tag_node?(record)
|
78
|
+
return not_empty_sub_records?(record) if type
|
79
|
+
each_tag_node do |node|
|
80
|
+
return true if node.contains_any_value_assigned_tag_node?(record)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def not_empty_sub_records?(record)
|
87
|
+
sub_records = record[type]
|
88
|
+
return false if sub_records.nil? or sub_records.empty?
|
89
|
+
sub_records.each do |rec|
|
90
|
+
return true if rec.values.any? {|val| val and not val.empty? }
|
91
|
+
end
|
92
|
+
false
|
93
|
+
end
|
90
94
|
end
|
91
95
|
|
92
|
-
class
|
96
|
+
class FallbackNode < TagNode
|
93
97
|
def assign_value_to_type(first_leaf)
|
94
98
|
return first_leaf unless first_leaf.kind_of? String
|
95
99
|
first_leaf.sub(/\A#{LINE_END_STR}/, '')
|
96
100
|
end
|
101
|
+
|
102
|
+
def contains_any_value_assigned_tag_node?(record)
|
103
|
+
false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class ValueNode < TagNode
|
108
|
+
def contains_any_value_assigned_tag_node?(record)
|
109
|
+
val = record[self.join.strip]
|
110
|
+
val and not val.empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
def contains_any_value_tag?
|
114
|
+
true
|
115
|
+
end
|
97
116
|
end
|
98
117
|
|
99
118
|
class Leaf < Parser::Leaf; end
|
@@ -120,26 +139,12 @@ module AdHocTemplate
|
|
120
139
|
|
121
140
|
def assign_type(tag, iteration_tag, fallback_tag)
|
122
141
|
node_tag_pairs = [
|
123
|
-
[
|
124
|
-
[
|
125
|
-
[
|
142
|
+
[ValueNode, *tag],
|
143
|
+
[IterationNode, *iteration_tag],
|
144
|
+
[FallbackNode, *fallback_tag]
|
126
145
|
]
|
127
146
|
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
def setup_attributes(node_tag_pairs)
|
134
|
-
@head, @tail, @head_of, @tail_of = {}, {}, {}, {}
|
135
|
-
|
136
|
-
node_tag_pairs.each do |node, tag|
|
137
|
-
head, tail = tag
|
138
|
-
@head[head] = node
|
139
|
-
@tail[tail] = node
|
140
|
-
@head_of[node] = head
|
141
|
-
@tail_of[node] = tail
|
142
|
-
end
|
147
|
+
@head, @tail, @head_of, @tail_of = PseudoHiki.associate_nodes_with_tags(node_tag_pairs)
|
143
148
|
end
|
144
149
|
|
145
150
|
register
|
@@ -172,7 +177,7 @@ module AdHocTemplate
|
|
172
177
|
end
|
173
178
|
|
174
179
|
def self.remove_indents_and_newlines_if_necessary(str, tag_name)
|
175
|
-
node_types = [
|
180
|
+
node_types = [IterationNode, FallbackNode]
|
176
181
|
tag_type = TagType[tag_name]
|
177
182
|
if TagType[tag_name].remove_iteration_indent
|
178
183
|
str = remove_indent_before_iteration_tags(str, tag_type)
|
@@ -183,8 +188,8 @@ module AdHocTemplate
|
|
183
188
|
|
184
189
|
def self.remove_indent_before_iteration_tags(template_source, tag_type)
|
185
190
|
start_tag, end_tag = [
|
186
|
-
tag_type.head_of[
|
187
|
-
tag_type.tail_of[
|
191
|
+
tag_type.head_of[IterationNode],
|
192
|
+
tag_type.tail_of[IterationNode],
|
188
193
|
].map {|tag| Regexp.escape(tag) }
|
189
194
|
template_source.gsub(/^([ \t]+#{start_tag}\S*#{LINE_END_STR})/) {|s| s.lstrip }
|
190
195
|
.gsub(/^([ \t]+#{end_tag}#{LINE_END_STR})/) {|s| s.lstrip }
|
@@ -192,8 +197,8 @@ module AdHocTemplate
|
|
192
197
|
|
193
198
|
def self.remove_indent_before_fallback_tags(template_source, tag_type)
|
194
199
|
tag_re_str = [
|
195
|
-
tag_type.head_of[
|
196
|
-
tag_type.tail_of[
|
200
|
+
tag_type.head_of[FallbackNode],
|
201
|
+
tag_type.tail_of[FallbackNode],
|
197
202
|
].map {|tag| Regexp.escape(tag) }.join('|')
|
198
203
|
template_source.gsub(/^([ \t]+(?:#{tag_re_str})#{LINE_END_STR})/) {|s| s.lstrip }
|
199
204
|
end
|