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.
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
+