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