ad_hoc_template 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ad_hoc_template.gemspec +1 -1
- data/lib/ad_hoc_template/command_line_interface.rb +53 -26
- data/lib/ad_hoc_template/config_manager.rb +129 -0
- data/lib/ad_hoc_template/default_tag_formatter.rb +6 -1
- data/lib/ad_hoc_template/entry_format_generator.rb +83 -12
- data/lib/ad_hoc_template/parser.rb +64 -59
- data/lib/ad_hoc_template/recipe_manager.rb +168 -0
- data/lib/ad_hoc_template/record_reader.rb +73 -16
- data/lib/ad_hoc_template/utils.rb +29 -0
- data/lib/ad_hoc_template/version.rb +1 -1
- data/lib/ad_hoc_template.rb +77 -25
- data/samples/en/inner_iteration/data.aht +21 -0
- data/samples/en/inner_iteration/data.csv +5 -0
- data/samples/en/inner_iteration/data.yaml +14 -0
- data/samples/en/inner_iteration/data2.yaml +14 -0
- data/samples/en/inner_iteration/for_csv.sh +3 -0
- data/samples/en/inner_iteration/template.html +18 -0
- data/samples/en/recipe/main.aht +9 -0
- data/samples/en/recipe/template.html +20 -0
- data/samples/for_recipe.sh +3 -0
- data/samples/ja/inner_iteration/data.aht +21 -0
- data/samples/ja/inner_iteration/data.csv +5 -0
- data/samples/ja/inner_iteration/data.yaml +14 -0
- data/samples/ja/inner_iteration/data2.yaml +14 -0
- data/samples/ja/inner_iteration/for_csv.sh +3 -0
- data/samples/ja/inner_iteration/template.html +18 -0
- data/samples/ja/recipe/main.aht +9 -0
- data/samples/ja/recipe/template.html +20 -0
- data/samples/recipe.yaml +34 -0
- data/spec/ad_hoc_template_spec.rb +71 -1
- data/spec/command_line_interface_spec.rb +105 -11
- data/spec/config_manager_spec.rb +142 -0
- data/spec/default_tag_formatter_spec.rb +13 -0
- data/spec/entry_format_generator_spec.rb +160 -17
- data/spec/parser_spec.rb +64 -20
- data/spec/recipe_manager_spec.rb +419 -0
- data/spec/record_reader_spec.rb +122 -1
- data/spec/test_data/en/inner_iteration/data.csv +5 -0
- data/spec/test_data/en/recipe/expected_result.html +32 -0
- data/spec/test_data/en/recipe/main.aht +9 -0
- data/spec/test_data/en/recipe/template.html +20 -0
- data/spec/test_data/ja/inner_iteration/data.csv +5 -0
- data/spec/test_data/ja/recipe/expected_result.html +32 -0
- data/spec/test_data/ja/recipe/main.aht +9 -0
- data/spec/test_data/ja/recipe/template.html +20 -0
- data/spec/test_data/recipe.yaml +34 -0
- metadata +47 -4
@@ -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
|
+
|