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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/ad_hoc_template.gemspec +1 -1
  3. data/lib/ad_hoc_template/command_line_interface.rb +53 -26
  4. data/lib/ad_hoc_template/config_manager.rb +129 -0
  5. data/lib/ad_hoc_template/default_tag_formatter.rb +6 -1
  6. data/lib/ad_hoc_template/entry_format_generator.rb +83 -12
  7. data/lib/ad_hoc_template/parser.rb +64 -59
  8. data/lib/ad_hoc_template/recipe_manager.rb +168 -0
  9. data/lib/ad_hoc_template/record_reader.rb +73 -16
  10. data/lib/ad_hoc_template/utils.rb +29 -0
  11. data/lib/ad_hoc_template/version.rb +1 -1
  12. data/lib/ad_hoc_template.rb +77 -25
  13. data/samples/en/inner_iteration/data.aht +21 -0
  14. data/samples/en/inner_iteration/data.csv +5 -0
  15. data/samples/en/inner_iteration/data.yaml +14 -0
  16. data/samples/en/inner_iteration/data2.yaml +14 -0
  17. data/samples/en/inner_iteration/for_csv.sh +3 -0
  18. data/samples/en/inner_iteration/template.html +18 -0
  19. data/samples/en/recipe/main.aht +9 -0
  20. data/samples/en/recipe/template.html +20 -0
  21. data/samples/for_recipe.sh +3 -0
  22. data/samples/ja/inner_iteration/data.aht +21 -0
  23. data/samples/ja/inner_iteration/data.csv +5 -0
  24. data/samples/ja/inner_iteration/data.yaml +14 -0
  25. data/samples/ja/inner_iteration/data2.yaml +14 -0
  26. data/samples/ja/inner_iteration/for_csv.sh +3 -0
  27. data/samples/ja/inner_iteration/template.html +18 -0
  28. data/samples/ja/recipe/main.aht +9 -0
  29. data/samples/ja/recipe/template.html +20 -0
  30. data/samples/recipe.yaml +34 -0
  31. data/spec/ad_hoc_template_spec.rb +71 -1
  32. data/spec/command_line_interface_spec.rb +105 -11
  33. data/spec/config_manager_spec.rb +142 -0
  34. data/spec/default_tag_formatter_spec.rb +13 -0
  35. data/spec/entry_format_generator_spec.rb +160 -17
  36. data/spec/parser_spec.rb +64 -20
  37. data/spec/recipe_manager_spec.rb +419 -0
  38. data/spec/record_reader_spec.rb +122 -1
  39. data/spec/test_data/en/inner_iteration/data.csv +5 -0
  40. data/spec/test_data/en/recipe/expected_result.html +32 -0
  41. data/spec/test_data/en/recipe/main.aht +9 -0
  42. data/spec/test_data/en/recipe/template.html +20 -0
  43. data/spec/test_data/ja/inner_iteration/data.csv +5 -0
  44. data/spec/test_data/ja/recipe/expected_result.html +32 -0
  45. data/spec/test_data/ja/recipe/main.aht +9 -0
  46. data/spec/test_data/ja/recipe/template.html +20 -0
  47. data/spec/test_data/recipe.yaml +34 -0
  48. metadata +47 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a41e80d33bd430c264ac2704bf7850baf3bcebc7
4
- data.tar.gz: f10b0463f915e33987e5ac04e4928737eba11fc2
3
+ metadata.gz: ddd8ddbfdc477efb70980b40c3e16848108df9ea
4
+ data.tar.gz: fb506b375a6781451f00f32c6a88b58a2fab210c
5
5
  SHA512:
6
- metadata.gz: 0b423fd73c6d59c11975fadddccdf56697a15f9784510277b23c4011c66b0d235b111285201ca24659c72b1456294990939a172bfd8998e86a441c57e16ac426
7
- data.tar.gz: bfb7de0f42fd16cba6e2c75ee0e2d7764c86cd2e4d135e38f1cc05cc271b9dc45681a351ddc84d68175d85c6019df23c99b94202efc650f981ce2b1063a56f10
6
+ metadata.gz: c5e5ef55188915bd037bf8b20e67b5a3226f54aa1037345510dc5a10319f4cf59711a7627c35c52c716f5633abe3170b9b6f09b56c04ec5b71a90b47c56746d9
7
+ data.tar.gz: 9246fcb22fabbab24a8866ef612474121d5acc42cb4fa65a333e717aac26cc4dc7a4ebdd9d708c99eb0cdf8294fc207d98c0a43a5c69e2a2459650700dd0598d
@@ -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.5.develop"
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.extract_labels(tree, @data_format)
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 ? generate_entry_format : render
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
- FUNCTION_TABLE[format_label] = func
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::IterationTagNode, Parser::FallbackTagNode
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.extract_labels(parsed_template, target_format=nil)
36
- labels = extract_labels_as_ruby_objects(parsed_template)
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.extract_labels_as_ruby_objects(parsed_template)
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
- private_class_method :extract_labels_as_ruby_objects
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
- self.select {|n| n.kind_of?(TagNode) }.each do |node|
22
- if node.kind_of? IterationTagNode
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
- select {|n| n.kind_of?(TagNode) }.each do |node|
34
- case node
35
- when IterationTagNode, FallbackTagNode
36
- return node.contains_any_value_tag?
37
- when TagNode
38
- return true
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
- false
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 IterationTagNode < TagNode
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(/:\s/, 2)
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 FallbackTagNode < TagNode
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
- [TagNode, tag],
124
- [IterationTagNode, iteration_tag],
125
- [FallbackTagNode, fallback_tag]
142
+ [ValueNode, *tag],
143
+ [IterationNode, *iteration_tag],
144
+ [FallbackNode, *fallback_tag]
126
145
  ]
127
146
 
128
- setup_attributes(node_tag_pairs)
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 = [IterationTagNode, FallbackTagNode]
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[IterationTagNode],
187
- tag_type.tail_of[IterationTagNode],
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[FallbackTagNode],
196
- tag_type.tail_of[FallbackTagNode],
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