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
@@ -0,0 +1,419 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'spec_helper'
4
+ require 'ad_hoc_template'
5
+ require 'ad_hoc_template/recipe_manager'
6
+
7
+ describe AdHocTemplate do
8
+ describe AdHocTemplate::RecipeManager do
9
+ before do
10
+ @recipe = <<RECIPE
11
+ ---
12
+ template: template.html
13
+ tag_type: :default
14
+ template_encoding: UTF-8
15
+ data: main.aht
16
+ data_format:
17
+ data_encoding:
18
+ output_file: output.html
19
+ blocks:
20
+ - label: "#authors"
21
+ data:
22
+ data_format:
23
+ data_encoding:
24
+ - label: "#authors|works|name"
25
+ data: authors.csv
26
+ data_format: csv
27
+ data_encoding: 'iso-8859-1'
28
+ - label: "#authors|bio|name"
29
+ data: authors.csv
30
+ data_format: csv
31
+ data_encoding: 'iso-8859-1'
32
+ RECIPE
33
+ @template = <<TEMPLATE
34
+ Title: Famous authors of <%= country %> literature
35
+
36
+ <%#authors:
37
+ Name: <%= name %>
38
+ Birthplace: <%= birth_place %>
39
+ Works:
40
+ <%#works|name:
41
+ * <%= title %>
42
+ #%>
43
+ <%#
44
+ <%#bio|name:
45
+ Born: <%= birth_date %>
46
+ #%>
47
+ #%>
48
+
49
+ #%>
50
+ TEMPLATE
51
+
52
+ @parsed_template = [
53
+ ["Title: Famous authors of "], [["country "]], [" literature\n\n"],
54
+ [["Name: "], [["name "]], ["\nBirthplace: "], [["birth_place "]],
55
+ ["\nWorks:\n"], [[" * "], [["title "]], ["\n"]], [[""],
56
+ [["Born: "], [["birth_date "]], ["\n"]]], ["\n"]]]
57
+
58
+ @main_data =<<MAIN_DATA
59
+ country: French
60
+
61
+ ///@#authors
62
+
63
+ name: Albert Camus
64
+ birth_place: Algeria
65
+
66
+ name: Marcel Ayme'
67
+ birth_place: France
68
+ MAIN_DATA
69
+
70
+ @csv_data =<<CSV_DATA
71
+ name,title
72
+ Albert Camus,"L'E'tranger"
73
+ Albert Camus,La Peste
74
+ "Marcel Ayme'",Le Passe-muraille
75
+ "Marcel Ayme'","Les Contes du chat perche'"
76
+ CSV_DATA
77
+
78
+ @expected_result =<<EXPECTED_RESULT
79
+ Title: Famous authors of French literature
80
+
81
+ Name: Albert Camus
82
+ Birthplace: Algeria
83
+ Works:
84
+ * L'E'tranger
85
+ * La Peste
86
+
87
+ Name: Marcel Ayme'
88
+ Birthplace: France
89
+ Works:
90
+ * Le Passe-muraille
91
+ * Les Contes du chat perche'
92
+
93
+ EXPECTED_RESULT
94
+ end
95
+
96
+ it 'reads a recipe' do
97
+ reader = AdHocTemplate::RecipeManager.new(@recipe)
98
+ recipe = reader.recipe
99
+
100
+ expect(recipe['blocks'][0]['data']).to eq('main.aht')
101
+ expect(recipe['blocks'][1]['data']).to eq('authors.csv')
102
+ end
103
+
104
+ it '#prepare_block_data reads data into a block from a source file' do
105
+ expected_result = {
106
+ "#authors" => [{"name"=>"Albert Camus"}, {"name"=>"Marcel Ayme'"}],
107
+ "#authors|works|Albert Camus" => [
108
+ {"name"=>"Albert Camus", "title"=>"L'E'tranger"},
109
+ {"name"=>"Albert Camus", "title"=>"La Peste"}],
110
+ "#authors|works|Marcel Ayme'" => [
111
+ {"name"=>"Marcel Ayme'", "title"=>"Le Passe-muraille"},
112
+ {"name"=>"Marcel Ayme'", "title"=>"Les Contes du chat perche'"}]}
113
+
114
+ reader = AdHocTemplate::RecipeManager.new(@recipe)
115
+ recipe = reader.recipe
116
+ block = recipe['blocks'][1]
117
+ data_file_path = File.expand_path(block['data'])
118
+ csv_data = StringIO.new(@csv_data)
119
+ open_mode = ['rb', block['data_encoding']].join(':')
120
+ allow(reader).to receive(:open).with(data_file_path, open_mode).and_yield(csv_data)
121
+ block_data = reader.prepare_block_data(block)
122
+ expect(block_data).to eq(expected_result)
123
+ end
124
+
125
+ it '#prepare_block_data guesses data_format from the extention of data file' do
126
+ recipe_source = <<RECIPE
127
+ ---
128
+ template: template.html
129
+ tag_type: :default
130
+ template_encoding: UTF-8
131
+ data: main.aht
132
+ data_format:
133
+ data_encoding:
134
+ output_file:
135
+ blocks:
136
+ - label: "#authors"
137
+ data:
138
+ data_format:
139
+ data_encoding:
140
+ - label: "#authors|works|name"
141
+ data: authors.csv
142
+ data_format:
143
+ data_encoding: 'iso-8859-1'
144
+ RECIPE
145
+
146
+ expected_result = {
147
+ "#authors" => [{"name"=>"Albert Camus"}, {"name"=>"Marcel Ayme'"}],
148
+ "#authors|works|Albert Camus" => [
149
+ {"name"=>"Albert Camus", "title"=>"L'E'tranger"},
150
+ {"name"=>"Albert Camus", "title"=>"La Peste"}],
151
+ "#authors|works|Marcel Ayme'" => [
152
+ {"name"=>"Marcel Ayme'", "title"=>"Le Passe-muraille"},
153
+ {"name"=>"Marcel Ayme'", "title"=>"Les Contes du chat perche'"}]}
154
+
155
+ reader = AdHocTemplate::RecipeManager.new(recipe_source)
156
+ recipe = reader.recipe
157
+ block = recipe['blocks'][1]
158
+ data_file_path = File.expand_path(block['data'])
159
+ csv_data = StringIO.new(@csv_data)
160
+ open_mode = ['rb', block['data_encoding']].join(':')
161
+ allow(reader).to receive(:open).with(data_file_path, open_mode).and_yield(csv_data)
162
+ block_data = reader.prepare_block_data(block)
163
+ expect(block_data).to eq(expected_result)
164
+ end
165
+
166
+ it '#load_records reads blocks and merge them' do
167
+ expected_result = {
168
+ "country" => "French",
169
+ "#authors" => [{"name"=>"Albert Camus", "birth_place"=>"Algeria"}, {"name"=>"Marcel Ayme'", "birth_place"=>"France"}],
170
+ "#authors|works|Albert Camus" => [
171
+ {"name"=>"Albert Camus", "title"=>"L'E'tranger"},
172
+ {"name"=>"Albert Camus", "title"=>"La Peste"}],
173
+ "#authors|works|Marcel Ayme'" => [
174
+ {"name"=>"Marcel Ayme'", "title"=>"Le Passe-muraille"},
175
+ {"name"=>"Marcel Ayme'", "title"=>"Les Contes du chat perche'"}]}
176
+
177
+ reader = AdHocTemplate::RecipeManager.new(@recipe)
178
+ recipe = reader.recipe
179
+ allow(reader).to receive(:open).with(File.expand_path(recipe['data']), 'rb:BOM|UTF-8').and_yield(StringIO.new(@main_data))
180
+ recipe['blocks'].each do |block|
181
+ data_file_path = File.expand_path(block['data'])
182
+ csv_data = StringIO.new(@csv_data)
183
+ open_mode = ['rb', block['data_encoding']].join(':')
184
+ allow(reader).to receive(:open).with(data_file_path, open_mode).and_yield(StringIO.new(@csv_data))
185
+ end
186
+ main_block = reader.load_records
187
+ expect(main_block).to eq(expected_result)
188
+ end
189
+
190
+ it '#load_records may read recipes without iteration blocks' do
191
+ recipe_source = <<RECIPE
192
+ ---
193
+ template: template.html
194
+ tag_type: :default
195
+ template_encoding: UTF-8
196
+ data: main.aht
197
+ data_format:
198
+ data_encoding:
199
+ output_file:
200
+ RECIPE
201
+
202
+ main_data = <<MAIN_DATA
203
+ country: French
204
+ century: 20
205
+
206
+ MAIN_DATA
207
+
208
+ expected_result = {
209
+ "country" => "French",
210
+ "century" => "20"
211
+ }
212
+
213
+ reader = AdHocTemplate::RecipeManager.new(recipe_source)
214
+ recipe = reader.recipe
215
+ allow(reader).to receive(:open).with(File.expand_path(recipe['data']), 'rb:BOM|UTF-8').and_yield(StringIO.new(main_data))
216
+
217
+ main_block = reader.load_records
218
+ expect(main_block).to eq(expected_result)
219
+ end
220
+
221
+ it '#load_records may read recipes of which #recipe["data"] is not specified' do
222
+ recipe_source = <<RECIPE
223
+ ---
224
+ template: template.html
225
+ tag_type: :default
226
+ template_encoding: UTF-8
227
+ data:
228
+ data_format:
229
+ data_encoding:
230
+ output_file:
231
+ blocks:
232
+ - label: "#authors"
233
+ data:
234
+ data_format:
235
+ data_encoding:
236
+ - label: "#authors|works|name"
237
+ data: authors.csv
238
+ data_format:
239
+ data_encoding: 'iso-8859-1'
240
+ RECIPE
241
+
242
+ expected_result = {
243
+ "#authors" => [{"name"=>"Albert Camus"}, {"name"=>"Marcel Ayme'"}],
244
+ "#authors|works|Albert Camus" => [
245
+ {"name"=>"Albert Camus", "title"=>"L'E'tranger"},
246
+ {"name"=>"Albert Camus", "title"=>"La Peste"}],
247
+ "#authors|works|Marcel Ayme'" => [
248
+ {"name"=>"Marcel Ayme'", "title"=>"Le Passe-muraille"},
249
+ {"name"=>"Marcel Ayme'", "title"=>"Les Contes du chat perche'"}]
250
+ }
251
+
252
+ reader = AdHocTemplate::RecipeManager.new(recipe_source)
253
+ recipe = reader.recipe
254
+ allow(reader).to receive(:open).with(File.expand_path(recipe['blocks'][1]['data']), 'rb:iso-8859-1').and_yield(StringIO.new(@csv_data))
255
+ main_block = reader.load_records
256
+
257
+ expect(main_block).to eq(expected_result)
258
+ end
259
+
260
+ it '#load_records may read recipes of which #recipe["data"] is not specified and outer iteration blocks are omitted' do
261
+ recipe_source = <<RECIPE
262
+ ---
263
+ template: template.html
264
+ tag_type: :default
265
+ template_encoding: UTF-8
266
+ data:
267
+ data_format:
268
+ data_encoding:
269
+ output_file:
270
+ blocks:
271
+ - label: "#authors|works|name"
272
+ data: authors.csv
273
+ data_format:
274
+ data_encoding: 'iso-8859-1'
275
+ RECIPE
276
+
277
+ expected_result = {
278
+ "#authors" => [{"name"=>"Albert Camus"}, {"name"=>"Marcel Ayme'"}],
279
+ "#authors|works|Albert Camus" => [
280
+ {"name"=>"Albert Camus", "title"=>"L'E'tranger"},
281
+ {"name"=>"Albert Camus", "title"=>"La Peste"}],
282
+ "#authors|works|Marcel Ayme'" => [
283
+ {"name"=>"Marcel Ayme'", "title"=>"Le Passe-muraille"},
284
+ {"name"=>"Marcel Ayme'", "title"=>"Les Contes du chat perche'"}]
285
+ }
286
+
287
+ reader = AdHocTemplate::RecipeManager.new(recipe_source)
288
+ recipe = reader.recipe
289
+ allow(reader).to receive(:open).with(File.expand_path(recipe['blocks'][0]['data']), 'rb:iso-8859-1').and_yield(StringIO.new(@csv_data))
290
+ main_block = reader.load_records
291
+
292
+ expect(main_block).to eq(expected_result)
293
+ end
294
+
295
+ it "the result of #load_records can be used as input of DataLoader.parse" do
296
+ reader = AdHocTemplate::RecipeManager.new(@recipe)
297
+ recipe = reader.recipe
298
+ allow(reader).to receive(:open).with(File.expand_path(recipe['data']), 'rb:BOM|UTF-8').and_yield(StringIO.new(@main_data))
299
+ recipe['blocks'].each do |block|
300
+ data_file_path = File.expand_path(block['data'])
301
+ csv_data = StringIO.new(@csv_data)
302
+ open_mode = ['rb', block['data_encoding']].join(':')
303
+ allow(reader).to receive(:open).with(data_file_path, open_mode).and_yield(StringIO.new(@csv_data))
304
+ end
305
+
306
+ main_block = reader.load_records
307
+ tree = AdHocTemplate::Parser.parse(@template)
308
+ result = AdHocTemplate::DataLoader.format(tree, main_block)
309
+ expect(result).to eq(@expected_result)
310
+ end
311
+
312
+ it "#parse_template parses the template file specified in the recipe" do
313
+ reader = AdHocTemplate::RecipeManager.new(@recipe)
314
+ template_path = File.expand_path(reader.recipe['template'])
315
+ open_mode = 'rb:BOM|UTF-8'
316
+ expect_any_instance_of(AdHocTemplate::RecipeManager).to receive(:open).with(template_path, open_mode).and_yield(StringIO.new(@template))
317
+
318
+ reader.parse_template
319
+
320
+ expect(reader.template).to eq(@parsed_template)
321
+ end
322
+
323
+ it "#update_output_file writes the result into an output file specified in the recipe" do
324
+ reader = AdHocTemplate::RecipeManager.new(@recipe)
325
+ recipe = reader.recipe
326
+
327
+ allow_any_instance_of(AdHocTemplate::RecipeManager).to receive(:open).with(File.expand_path(recipe['data']), 'rb:BOM|UTF-8').and_yield(StringIO.new(@main_data))
328
+ recipe['blocks'].each do |block|
329
+ data_file_path = File.expand_path(block['data'])
330
+ csv_data = StringIO.new(@csv_data)
331
+ open_mode = block['data_encoding'] ? ['rb', block['data_encoding']].join(':') : 'rb:BOM|UTF-8'
332
+ expect_any_instance_of(AdHocTemplate::RecipeManager).to receive(:open).with(data_file_path, open_mode).and_yield(StringIO.new(@csv_data))
333
+ end
334
+
335
+ template_path = File.expand_path(reader.recipe['template'])
336
+ open_mode = 'rb:BOM|UTF-8'
337
+ output_file_path = File.expand_path(reader.recipe['output_file'])
338
+ output_file = StringIO.new(@template)
339
+ expect_any_instance_of(AdHocTemplate::RecipeManager).to receive(:open).with(template_path, open_mode).and_yield(StringIO.new(@template))
340
+ expect_any_instance_of(AdHocTemplate::RecipeManager).to receive(:open).with(output_file_path, 'wb:UTF-8').and_yield(output_file)
341
+
342
+ reader.update_output_file
343
+ expect(output_file.string).to eq(@expected_result)
344
+ end
345
+
346
+ describe '#modified_after_last_output?' do
347
+ before do
348
+ recipe_source = File.read('spec/test_data/recipe.yaml')
349
+ @recipe = AdHocTemplate::RecipeManager.new(recipe_source)
350
+ @near_average_time = File.mtime(@recipe.recipe['template'])
351
+ @newest_file_time = @near_average_time + 3600
352
+ @oldest_file_time = @near_average_time - 3600
353
+ @output_path = File.expand_path(@recipe.recipe['output_file'])
354
+ end
355
+
356
+ it 'returns true when the output file does not exist' do
357
+ allow(File).to receive(:exist?).with(@output_path).and_return(false)
358
+
359
+ expect(@recipe.modified_after_last_output?).to be_truthy
360
+ end
361
+
362
+ it 'returns true when the output file is older than the template file' do
363
+ allow(File).to receive(:exist?).with(@output_path).and_return(true)
364
+ allow(File).to receive(:mtime).with(@output_path).and_return(@near_average_time)
365
+ allow(File).to receive(:mtime).with(File.expand_path(@recipe.recipe['template'])).and_return(@newest_file_time)
366
+ @recipe.recipe['blocks'].each do |block|
367
+ allow(File).to receive(:mtime).with(File.expand_path(block['data'])).and_return(@oldest_file_time)
368
+ end
369
+
370
+ expect(@recipe.modified_after_last_output?).to be_truthy
371
+ end
372
+
373
+ it 'returns true when the output file is older than data files' do
374
+ allow(File).to receive(:exist?).with(@output_path).and_return(true)
375
+ allow(File).to receive(:mtime).with(@output_path).and_return(@near_average_time)
376
+ allow(File).to receive(:mtime).with(File.expand_path(@recipe.recipe['template'])).and_return(@oldest_file_time)
377
+ @recipe.recipe['blocks'].each do |block|
378
+ allow(File).to receive(:mtime).with(File.expand_path(block['data'])).and_return(@newest_file_time)
379
+ end
380
+
381
+ expect(@recipe.modified_after_last_output?).to be_truthy
382
+ end
383
+
384
+ it 'returns true when RecipeManager#output_file returns nil' do
385
+ recipe_source = <<RECIPE
386
+ ---
387
+ template: template.html
388
+ tag_type: :default
389
+ template_encoding: UTF-8
390
+ data: main.aht
391
+ data_format:
392
+ data_encoding:
393
+ output_file:
394
+ blocks:
395
+ - label: "#authors"
396
+ data:
397
+ data_format:
398
+ data_encoding:
399
+ RECIPE
400
+
401
+ recipe = AdHocTemplate::RecipeManager.new(recipe_source)
402
+
403
+ expect(recipe.modified_after_last_output?).to be_truthy
404
+ end
405
+
406
+ it 'returns false when the output file is the newest file' do
407
+ allow(File).to receive(:exist?).with(@output_path).and_return(true)
408
+ allow(File).to receive(:mtime).with(@output_path).and_return(@newest_file_time)
409
+ allow(File).to receive(:mtime).with(File.expand_path(@recipe.recipe['template'])).and_return(@near_average_time)
410
+ @recipe.recipe['blocks'].each do |block|
411
+ allow(File).to receive(:mtime).with(File.expand_path(block['data'])).and_return(@near_average_time)
412
+ end
413
+
414
+ expect(@recipe.modified_after_last_output?).to be_falsy
415
+ end
416
+ end
417
+ end
418
+ end
419
+
@@ -149,6 +149,50 @@ expected_config = {
149
149
  }
150
150
  expect(AdHocTemplate::RecordReader.read_record(data)).to eq(expected_config)
151
151
  end
152
+
153
+ it "may contain blocks with comments" do
154
+ data = <<CONFIG
155
+ //// comment1 in key-value block
156
+
157
+ key1: value1
158
+ //// comment2 in key-value block
159
+ key2: value2
160
+ key3: value3
161
+ //// comment3 in key-value block
162
+
163
+ ///@#subconfigs
164
+ ////comment1 in iteration block
165
+
166
+ ////comment2 in iteration block
167
+ key1-1: value1-1
168
+ key1-2: value1-2
169
+
170
+ key2-1: value2-1
171
+ key2-2: value2-2
172
+
173
+ ////comment3 in iteration block
174
+ ///@block
175
+
176
+ ////comment1 in block
177
+
178
+ ////comment2 in block
179
+ the first line of block
180
+ the second line of block
181
+
182
+ ////comment-like line
183
+ the second paragraph in block
184
+
185
+ CONFIG
186
+
187
+ expected_config = {
188
+ "key1" => "value1",
189
+ "key2" => "value2",
190
+ "key3" => "value3",
191
+ "#subconfigs" => [{"key1-1"=>"value1-1", "key1-2"=>"value1-2"}, {"key2-1"=>"value2-1", "key2-2"=>"value2-2"}],
192
+ "block" => "the first line of block\nthe second line of block\n\n////comment-like line\nthe second paragraph in block\n"
193
+ }
194
+ expect(AdHocTemplate::RecordReader.read_record(data)).to eq(expected_config)
195
+ end
152
196
  end
153
197
 
154
198
  describe AdHocTemplate::RecordReader::YAMLReader do
@@ -235,6 +279,27 @@ YAML
235
279
 
236
280
  expect(yaml).to eq(@yaml_dump)
237
281
  end
282
+
283
+ it 'may contain key-value pairs whose value are not String' do
284
+ yaml_source = <<YAML
285
+ ---
286
+ key1: 1
287
+ key2: 2
288
+ "#iterate":
289
+ - key3: 3
290
+ - key3: 4
291
+ YAML
292
+
293
+ template = '<%= key1 %> and <%h key2 %><%#iterate: <%= key3 %>'
294
+ expected_result = '1 and 2 3 4'
295
+
296
+ yaml = AdHocTemplate::RecordReader::YAMLReader.read_record(yaml_source)
297
+ tree = AdHocTemplate::Parser.parse(template)
298
+ tag_formatter = AdHocTemplate::DefaultTagFormatter.new
299
+ result = AdHocTemplate::DataLoader.format(tree, yaml, tag_formatter)
300
+
301
+ expect(result).to eq(expected_result)
302
+ end
238
303
  end
239
304
 
240
305
  describe AdHocTemplate::RecordReader::JSONReader do
@@ -327,6 +392,33 @@ JSON
327
392
 
328
393
  expect(json).to eq(@json_dump.chomp)
329
394
  end
395
+
396
+ it 'may contain key-value pairs whose value are not String' do
397
+ json_source = <<JSON
398
+ {
399
+ "key1": 1,
400
+ "key2": 2,
401
+ "#iterate": [
402
+ {
403
+ "key3": 3
404
+ },
405
+ {
406
+ "key3": 4
407
+ }
408
+ ]
409
+ }
410
+ JSON
411
+
412
+ template = '<%= key1 %> and <%h key2 %><%#iterate: <%= key3 %>'
413
+ expected_result = '1 and 2 3 4'
414
+
415
+ json = AdHocTemplate::RecordReader::JSONReader.read_record(json_source)
416
+ tree = AdHocTemplate::Parser.parse(template)
417
+ tag_formatter = AdHocTemplate::DefaultTagFormatter.new
418
+ result = AdHocTemplate::DataLoader.format(tree, json, tag_formatter)
419
+
420
+ expect(result).to eq(expected_result)
421
+ end
330
422
  end
331
423
 
332
424
  describe AdHocTemplate::RecordReader::CSVReader do
@@ -468,6 +560,35 @@ CSV
468
560
  expect(csv).to eq(config)
469
561
  end
470
562
 
563
+ it 'reads CSV data and arrange it for a pivot table like view' do
564
+ csv = <<CSV
565
+ name,title,birth_place
566
+ Albert Camus,"L'E'tranger",Algeria
567
+ Albert Camus,La Peste,Algeria
568
+ "Marcel Ayme'",Le Passe-muraille,France
569
+ "Marcel Ayme'","Les Contes du chat perche'",France
570
+ CSV
571
+
572
+ expected_result = {
573
+ '#authors' => [
574
+ { 'name' => 'Albert Camus' },
575
+ { 'name' => "Marcel Ayme'" }
576
+ ],
577
+ '#authors|works|Albert Camus' => [
578
+ { "name" => "Albert Camus", "title" => "L'E'tranger", "birth_place" => "Algeria" },
579
+ { "name" => "Albert Camus", "title" => "La Peste", "birth_place" => "Algeria" }
580
+ ],
581
+ "#authors|works|Marcel Ayme'" => [
582
+ { "name" => "Marcel Ayme'", "title" => "Le Passe-muraille", "birth_place" => "France" },
583
+ { "name" => "Marcel Ayme'", "title" => "Les Contes du chat perche'", "birth_place" => "France" }
584
+ ]
585
+ }
586
+
587
+ result = AdHocTemplate::RecordReader::CSVReader.read_record(csv, "authors|works|name")
588
+
589
+ expect(result).to eq(expected_result)
590
+ end
591
+
471
592
  it '.read_record is called from RecordReader.read_record if the format of source data is specified' do
472
593
  csv_reader = AdHocTemplate::RecordReader::CSVReader.read_record(@csv_source, "subconfigs")
473
594
  record_reader = AdHocTemplate::RecordReader.read_record(@csv_source, csv: "subconfigs")
@@ -550,7 +671,7 @@ block: |
550
671
  YAML
551
672
  end
552
673
 
553
- it ',dump accepts non-empty data' do
674
+ it '.dump accepts non-empty data' do
554
675
  parsed_data = AdHocTemplate::RecordReader::YAMLReader.read_record(@yaml_source)
555
676
  dump_data = AdHocTemplate::RecordReader::DefaultFormReader.dump(parsed_data)
556
677
  expected_data = @config_source.sub(/(#{$/}+)\Z/, $/)
@@ -0,0 +1,5 @@
1
+ name,title,year
2
+ Albert Camus,L'Étranger,1942
3
+ Albert Camus,La Peste,1947
4
+ Marcel Aymé,Le Passe-muraille,1943
5
+ Marcel Aymé,Les Contes du chat perché,1934-1946
@@ -0,0 +1,32 @@
1
+ <h1>Famous authors of French literature</h1>
2
+
3
+ <h2>Albert Camus</h2>
4
+
5
+ <p>Born in 1913</p>
6
+
7
+ <table summary="List of Albert Camus&#39;s famous works with publication year">
8
+ <caption>Famous works</caption>
9
+ <thead>
10
+ <tr><th scope="col">Title</th><th scope="col">Publication Year</th></tr>
11
+ </thead>
12
+ <tbody>
13
+ <tr><td>L'Étranger</td><td>1942</td></tr>
14
+ <tr><td>La Peste</td><td>1947</td></tr>
15
+ </tbody>
16
+ </table>
17
+
18
+ <h2>Marcel Aymé</h2>
19
+
20
+ <p>Born in 1902</p>
21
+
22
+ <table summary="List of Marcel Aymé&#39;s famous works with publication year">
23
+ <caption>Famous works</caption>
24
+ <thead>
25
+ <tr><th scope="col">Title</th><th scope="col">Publication Year</th></tr>
26
+ </thead>
27
+ <tbody>
28
+ <tr><td>Le Passe-muraille</td><td>1943</td></tr>
29
+ <tr><td>Les Contes du chat perché</td><td>1934-1946</td></tr>
30
+ </tbody>
31
+ </table>
32
+
@@ -0,0 +1,9 @@
1
+ country: French
2
+
3
+ ///@#authors
4
+
5
+ name: Albert Camus
6
+ birth_year: 1913
7
+
8
+ name: Marcel Aymé
9
+ birth_year: 1902
@@ -0,0 +1,20 @@
1
+ <h1>Famous authors of <!--%h country %--> literature</h1>
2
+
3
+ <!--%iterate%-->authors:
4
+ <h2><!--%h name %--></h2>
5
+
6
+ <p>Born in <!--%h birth_year %--></p>
7
+
8
+ <table summary="List of <!--%h name %-->&#39;s famous works with publication year">
9
+ <caption>Famous works</caption>
10
+ <thead>
11
+ <tr><th scope="col">Title</th><th scope="col">Publication Year</th></tr>
12
+ </thead>
13
+ <tbody>
14
+ <!--%iterate%-->works|name:
15
+ <tr><td><!--%h title %--></td><td><!--%h year %--></td></tr>
16
+ <!--%/iterate%-->
17
+ </tbody>
18
+ </table>
19
+
20
+ <!--%/iterate%-->
@@ -0,0 +1,5 @@
1
+ name,title,year
2
+ アルベール・カミユ,異邦人,1942
3
+ アルベール・カミユ,ペスト,1947
4
+ マルセル・エイメ,壁抜け男,1943
5
+ マルセル・エイメ,おにごっこ物語,1934-1946
@@ -0,0 +1,32 @@
1
+ <h1>フランス文学の著名な作家</h1>
2
+
3
+ <h2>アルベール・カミユ</h2>
4
+
5
+ <p>生年: 1913</p>
6
+
7
+ <table summary="アルベール・カミユの著名な作品とその出版年のリスト">
8
+ <caption>著名な作品</caption>
9
+ <thead>
10
+ <tr><th scope="col">作品名</th><th scope="col">出版年</th></tr>
11
+ </thead>
12
+ <tbody>
13
+ <tr><td>異邦人</td><td>1942</td></tr>
14
+ <tr><td>ペスト</td><td>1947</td></tr>
15
+ </tbody>
16
+ </table>
17
+
18
+ <h2>マルセル・エイメ</h2>
19
+
20
+ <p>生年: 1902</p>
21
+
22
+ <table summary="マルセル・エイメの著名な作品とその出版年のリスト">
23
+ <caption>著名な作品</caption>
24
+ <thead>
25
+ <tr><th scope="col">作品名</th><th scope="col">出版年</th></tr>
26
+ </thead>
27
+ <tbody>
28
+ <tr><td>壁抜け男</td><td>1943</td></tr>
29
+ <tr><td>おにごっこ物語</td><td>1934-1946</td></tr>
30
+ </tbody>
31
+ </table>
32
+