ad_hoc_template 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ad_hoc_template.gemspec +1 -1
- data/lib/ad_hoc_template/command_line_interface.rb +53 -26
- data/lib/ad_hoc_template/config_manager.rb +129 -0
- data/lib/ad_hoc_template/default_tag_formatter.rb +6 -1
- data/lib/ad_hoc_template/entry_format_generator.rb +83 -12
- data/lib/ad_hoc_template/parser.rb +64 -59
- data/lib/ad_hoc_template/recipe_manager.rb +168 -0
- data/lib/ad_hoc_template/record_reader.rb +73 -16
- data/lib/ad_hoc_template/utils.rb +29 -0
- data/lib/ad_hoc_template/version.rb +1 -1
- data/lib/ad_hoc_template.rb +77 -25
- data/samples/en/inner_iteration/data.aht +21 -0
- data/samples/en/inner_iteration/data.csv +5 -0
- data/samples/en/inner_iteration/data.yaml +14 -0
- data/samples/en/inner_iteration/data2.yaml +14 -0
- data/samples/en/inner_iteration/for_csv.sh +3 -0
- data/samples/en/inner_iteration/template.html +18 -0
- data/samples/en/recipe/main.aht +9 -0
- data/samples/en/recipe/template.html +20 -0
- data/samples/for_recipe.sh +3 -0
- data/samples/ja/inner_iteration/data.aht +21 -0
- data/samples/ja/inner_iteration/data.csv +5 -0
- data/samples/ja/inner_iteration/data.yaml +14 -0
- data/samples/ja/inner_iteration/data2.yaml +14 -0
- data/samples/ja/inner_iteration/for_csv.sh +3 -0
- data/samples/ja/inner_iteration/template.html +18 -0
- data/samples/ja/recipe/main.aht +9 -0
- data/samples/ja/recipe/template.html +20 -0
- data/samples/recipe.yaml +34 -0
- data/spec/ad_hoc_template_spec.rb +71 -1
- data/spec/command_line_interface_spec.rb +105 -11
- data/spec/config_manager_spec.rb +142 -0
- data/spec/default_tag_formatter_spec.rb +13 -0
- data/spec/entry_format_generator_spec.rb +160 -17
- data/spec/parser_spec.rb +64 -20
- data/spec/recipe_manager_spec.rb +419 -0
- data/spec/record_reader_spec.rb +122 -1
- data/spec/test_data/en/inner_iteration/data.csv +5 -0
- data/spec/test_data/en/recipe/expected_result.html +32 -0
- data/spec/test_data/en/recipe/main.aht +9 -0
- data/spec/test_data/en/recipe/template.html +20 -0
- data/spec/test_data/ja/inner_iteration/data.csv +5 -0
- data/spec/test_data/ja/recipe/expected_result.html +32 -0
- data/spec/test_data/ja/recipe/main.aht +9 -0
- data/spec/test_data/ja/recipe/template.html +20 -0
- data/spec/test_data/recipe.yaml +34 -0
- metadata +47 -4
@@ -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
|
+
|
data/spec/record_reader_spec.rb
CHANGED
@@ -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 '
|
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,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'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é'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,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 %-->'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,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
|
+
|