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,3 @@
1
+ #!/bin/sh
2
+
3
+ ad_hoc_template -t xml_comment_like -d 'csv:authors|works|name' template.html data.csv
@@ -0,0 +1,18 @@
1
+ <h1>作家</h1>
2
+
3
+ <!--%iterate%-->authors:
4
+ <h2><!--%h name %--></h2>
5
+
6
+ <table summary="<!--%h name %-->の著名な作品とその出版年のリスト">
7
+ <caption>著名な作品</caption>
8
+ <thead>
9
+ <tr><th scope="col">作品名</th><th scope="col">出版年</th></tr>
10
+ </thead>
11
+ <tbody>
12
+ <!--%iterate%-->works|name:
13
+ <tr><td><!--%h title %--></td><td><!--%h year %--></td></tr>
14
+ <!--%/iterate%-->
15
+ </tbody>
16
+ </table>
17
+
18
+ <!--%/iterate%-->
@@ -0,0 +1,9 @@
1
+ country: フランス
2
+
3
+ ///@#authors
4
+
5
+ name: アルベール・カミユ
6
+ birth_year: 1913
7
+
8
+ name: マルセル・エイメ
9
+ birth_year: 1902
@@ -0,0 +1,20 @@
1
+ <h1><!--%h country %-->文学の著名な作家</h1>
2
+
3
+ <!--%iterate%-->authors:
4
+ <h2><!--%h name %--></h2>
5
+
6
+ <p>生年: <!--%h birth_year %--></p>
7
+
8
+ <table summary="<!--%h name %-->の著名な作品とその出版年のリスト">
9
+ <caption>著名な作品</caption>
10
+ <thead>
11
+ <tr><th scope="col">作品名</th><th scope="col">出版年</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,34 @@
1
+ ---
2
+ template: en/recipe/template.html
3
+ tag_type: :xml_comment_like
4
+ template_encoding: UTF-8
5
+ data: en/recipe/main.aht
6
+ data_format:
7
+ data_encoding: UTF-8
8
+ output_file:
9
+ blocks:
10
+ - label: "#authors"
11
+ data:
12
+ data_format:
13
+ data_encoding:
14
+ - label: "#authors|works|name"
15
+ data: en/inner_iteration/data.csv
16
+ data_format:
17
+ data_encoding:
18
+ ---
19
+ template: ja/recipe/template.html
20
+ tag_type: :xml_comment_like
21
+ template_encoding: UTF-8
22
+ data: ja/recipe/main.aht
23
+ data_format:
24
+ data_encoding:
25
+ output_file:
26
+ blocks:
27
+ - label: "#authors"
28
+ data:
29
+ data_format:
30
+ data_encoding:
31
+ - label: "#authors|works|name"
32
+ data: ja/inner_iteration/data.csv
33
+ data_format:
34
+ data_encoding:
@@ -142,7 +142,7 @@ RESULT
142
142
  expect(AdHocTemplate::DataLoader.format(tree, config, tag_formatter)).to eq(expected_result)
143
143
  end
144
144
 
145
- it("should not add a newline at the head of IterationTagNode when the type of the node is not specified") do
145
+ it("should not add a newline at the head of IterationNode when the type of the node is not specified") do
146
146
  template = <<TEMPLATE
147
147
  a test string with tags
148
148
  <%#iteration_block:
@@ -244,6 +244,66 @@ The value of optional key2 is value2
244
244
  RESULT
245
245
  end
246
246
 
247
+ describe "nested iteration block" do
248
+ it "may contain inner iteration blocks" do
249
+ template =<<TEMPLATE
250
+ <%#authors:
251
+ Name: <%= name %>
252
+ Birthplace: <%= birthplace %>
253
+ Works:
254
+ <%#works|name:
255
+ * <%= title %>
256
+ #%>
257
+
258
+ #%>
259
+ TEMPLATE
260
+ data =<<DATA
261
+ ///@#authors
262
+
263
+ name: Albert Camus
264
+ birthplace: Algeria
265
+
266
+ name: Marcel Ayme'
267
+ birthplace: France
268
+
269
+ ///@#authors|works|Albert Camus
270
+
271
+ title: L'E'tranger
272
+
273
+ title: La Peste
274
+
275
+ ///@#authors|works|Marcel Ayme'
276
+
277
+ title: Le Passe-muraille
278
+
279
+ title: Les Contes du chat perche'
280
+
281
+ DATA
282
+
283
+ expected_result =<<RESULT
284
+ Name: Albert Camus
285
+ Birthplace: Algeria
286
+ Works:
287
+ * L'E'tranger
288
+ * La Peste
289
+
290
+ Name: Marcel Ayme'
291
+ Birthplace: France
292
+ Works:
293
+ * Le Passe-muraille
294
+ * Les Contes du chat perche'
295
+
296
+ RESULT
297
+
298
+ tree = AdHocTemplate::Parser.parse(template)
299
+ config = AdHocTemplate::RecordReader.read_record(data)
300
+ tag_formatter = AdHocTemplate::DefaultTagFormatter.new
301
+ result = AdHocTemplate::DataLoader.format(tree, config, tag_formatter)
302
+
303
+ expect(result).to eq(expected_result)
304
+ end
305
+ end
306
+
247
307
  describe "with data in default format" do
248
308
  it "should not ignore the content of an iteration block when some data are provided" do
249
309
  config_data = <<CONFIG
@@ -559,4 +619,14 @@ RESULT
559
619
  'a string with characters (<%h characters %>) that should be represented as character entities.')
560
620
  expect(result).to eq('a string with characters (&amp;, &quot;, &lt; and &gt;) that should be represented as character entities.')
561
621
  end
622
+
623
+ describe '.local_settings' do
624
+ it 'evaluates the given block in the context of ConfigManager' do
625
+ eval_result = AdHocTemplate.local_settings do
626
+ self::SETTINGS_FILE_NAME
627
+ end
628
+
629
+ expect(eval_result).to eq(AdHocTemplate::ConfigManager::SETTINGS_FILE_NAME)
630
+ end
631
+ end
562
632
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'shellwords'
4
4
  require 'stringio'
5
+ require 'fileutils'
5
6
  require 'spec_helper'
6
7
  require 'ad_hoc_template'
7
8
  require 'ad_hoc_template/command_line_interface'
@@ -60,11 +61,18 @@ RESULT
60
61
  end
61
62
 
62
63
  it "can set the internal/external encoding from the command line" do
63
- command_line_interface = AdHocTemplate::CommandLineInterface.new
64
- set_argv("-E UTF-8:Shift_JIS")
65
- command_line_interface.parse_command_line_options
66
- expect(Encoding.default_external.names).to include("UTF-8")
67
- expect(Encoding.default_internal.names).to include("Shift_JIS")
64
+ begin
65
+ default_external = Encoding.default_external
66
+ default_internal = Encoding.default_internal
67
+ command_line_interface = AdHocTemplate::CommandLineInterface.new
68
+ set_argv("-E UTF-8:Shift_JIS")
69
+ command_line_interface.parse_command_line_options
70
+ expect(Encoding.default_external.names).to include("UTF-8")
71
+ expect(Encoding.default_internal.names).to include("Shift_JIS")
72
+ ensure
73
+ Encoding.default_external = default_external
74
+ Encoding.default_internal = default_internal
75
+ end
68
76
  end
69
77
 
70
78
  it "can specify the output file from command line" do
@@ -231,6 +239,11 @@ key1,key2,key3
231
239
  value1-1,value1-2,value1-3
232
240
  value2-1,value2-2,value2-3
233
241
  value3-1,value3-2,value3-3
242
+ CSV
243
+ @record_in_csv_with_left_header = <<CSV
244
+ key1,value1-1,value2-1,value3-1
245
+ key2,value1-2,value2-2,value3-2
246
+ key3,value1-3,value2-3,value3-3
234
247
  CSV
235
248
 
236
249
  @expected_result = <<RESULT
@@ -301,7 +314,7 @@ TEMPLATE
301
314
  record_filename = "record.csv"
302
315
 
303
316
  allow(File).to receive(:read).with(File.expand_path(template_filename)).and_return(@template_without_iteration_block)
304
- allow(File).to receive(:read).with(File.expand_path(record_filename)).and_return(@record_in_csv_format)
317
+ allow(File).to receive(:read).with(File.expand_path(record_filename)).and_return(@record_in_csv_with_left_header)
305
318
  allow(STDOUT).to receive(:print).with(@expected_result)
306
319
 
307
320
  set_argv("--data-format=csv #{template_filename} #{record_filename}")
@@ -313,8 +326,9 @@ TEMPLATE
313
326
 
314
327
  it "can read csv data of only one record" do
315
328
  record_in_csv_format = <<CSV
316
- key1,key2,key3
317
- value1-1,value1-2,value1-3
329
+ key1,value1-1
330
+ key2,value1-2
331
+ key3,value1-3
318
332
  CSV
319
333
 
320
334
  expected_result = <<RESULT
@@ -346,6 +360,12 @@ key1 key2 key3
346
360
  value1-1 value1-2 value1-3
347
361
  value2-1 value2-2 value2-3
348
362
  value3-1 value3-2 value3-3
363
+ TSV
364
+
365
+ @record_in_tsv_with_left_header = <<TSV
366
+ key1 value1-1 value2-1 value3-1
367
+ key2 value1-2 value2-2 value3-2
368
+ key3 value1-3 value2-3 value3-3
349
369
  TSV
350
370
 
351
371
  @expected_result = <<RESULT
@@ -438,7 +458,7 @@ TEMPLATE
438
458
  record_filename = "record.tsv"
439
459
 
440
460
  allow(File).to receive(:read).with(File.expand_path(template_filename)).and_return(@template_without_iteration_block)
441
- allow(File).to receive(:read).with(File.expand_path(record_filename)).and_return(@record_in_tsv_format)
461
+ allow(File).to receive(:read).with(File.expand_path(record_filename)).and_return(@record_in_tsv_with_left_header)
442
462
  allow(STDOUT).to receive(:print).with(@expected_result)
443
463
 
444
464
  set_argv("--data-format=tsv #{template_filename} #{record_filename}")
@@ -450,8 +470,9 @@ TEMPLATE
450
470
 
451
471
  it "can read tsv data of only one record" do
452
472
  record_in_tsv_format = <<TSV
453
- key1 key2 key3
454
- value1-1 value1-2 value1-3
473
+ key1 value1-1
474
+ key2 value1-2
475
+ key3 value1-3
455
476
  TSV
456
477
 
457
478
  expected_result = <<RESULT
@@ -564,5 +585,78 @@ TSV
564
585
  command_line_interface.execute
565
586
  end
566
587
  end
588
+
589
+ describe '--recipe-template' do
590
+ it 'reads template files and geperates a blank recipe' do
591
+ expected_result = <<RECIPE
592
+ ---
593
+ template: spec/test_data/en/recipe/template.html
594
+ tag_type: :xml_comment_like
595
+ template_encoding: UTF-8
596
+ data:
597
+ data_format:
598
+ data_encoding:
599
+ output_file:
600
+ blocks:
601
+ - label: "#authors"
602
+ data:
603
+ data_format:
604
+ data_encoding:
605
+ - label: "#authors|works|name"
606
+ data:
607
+ data_format:
608
+ data_encoding:
609
+ ---
610
+ template: spec/test_data/ja/recipe/template.html
611
+ tag_type: :xml_comment_like
612
+ template_encoding: UTF-8
613
+ data:
614
+ data_format:
615
+ data_encoding:
616
+ output_file:
617
+ blocks:
618
+ - label: "#authors"
619
+ data:
620
+ data_format:
621
+ data_encoding:
622
+ - label: "#authors|works|name"
623
+ data:
624
+ data_format:
625
+ data_encoding:
626
+ RECIPE
627
+
628
+ allow(STDOUT).to receive(:print).with(expected_result)
629
+
630
+ set_argv("-R -t xml_comment_like spec/test_data/en/recipe/template.html spec/test_data/ja/recipe/template.html")
631
+ command_line_interface = AdHocTemplate::CommandLineInterface.new
632
+ command_line_interface.parse_command_line_options
633
+
634
+ command_line_interface.execute
635
+ end
636
+ end
637
+
638
+ describe '--cooking-recipe' do
639
+ it 'reads a recipe and updates output files' do
640
+ en_expected_result = File.read('spec/test_data/en/recipe/expected_result.html')
641
+ ja_expected_result = File.read('spec/test_data/ja/recipe/expected_result.html')
642
+ en_result_path = 'spec/test_data/en/recipe/result.html'
643
+ ja_result_path = 'spec/test_data/ja/recipe/result.html'
644
+
645
+ set_argv("-c spec/test_data/recipe.yaml")
646
+ command_line_interface = AdHocTemplate::CommandLineInterface.new
647
+ command_line_interface.parse_command_line_options
648
+
649
+ command_line_interface.execute
650
+
651
+ en_result = File.read(en_result_path)
652
+ ja_result = File.read(ja_result_path)
653
+
654
+ expect(en_result).to eq(en_expected_result)
655
+ expect(ja_result).to eq(ja_expected_result)
656
+
657
+ FileUtils.rm(en_result_path)
658
+ FileUtils.rm(ja_result_path)
659
+ end
660
+ end
567
661
  end
568
662
  end
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'shellwords'
4
+ require 'stringio'
5
+ require 'spec_helper'
6
+ require 'ad_hoc_template'
7
+ require 'ad_hoc_template/config_manager'
8
+
9
+ describe AdHocTemplate do
10
+ describe AdHocTemplate::ConfigManager do
11
+ describe '.require_local_settings' do
12
+ let(:fake_file) { double('file') }
13
+
14
+ before do
15
+ @config_file = AdHocTemplate::ConfigManager::LOCAL_SETTINGS_FILE
16
+ @config_full_path = File.expand_path(@config_file)
17
+ end
18
+
19
+ it 'loads local settings when the config file exists' do
20
+ stub_const("File", fake_file)
21
+ allow(File).to receive(:expand_path).with(@config_file).and_return(@config_full_path)
22
+ allow(File).to receive(:exist?).with(@config_full_path).and_return(true)
23
+ allow(AdHocTemplate::ConfigManager).to receive(:require).with(@config_full_path)
24
+ AdHocTemplate::ConfigManager.require_local_settings
25
+ end
26
+
27
+ it 'does not do nothing if the config file is not available' do
28
+ stub_const("File", fake_file)
29
+ allow(File).to receive(:expand_path).with(@config_file).and_return(@config_full_path)
30
+ allow(File).to receive(:exist?).with(@config_full_path).and_return(false)
31
+ allow(AdHocTemplate::ConfigManager).to receive(:require).with(@config_full_path)
32
+ AdHocTemplate::ConfigManager.require_local_settings
33
+ expect(AdHocTemplate::ConfigManager).to_not have_received(:require).with(@config_full_path)
34
+ end
35
+ end
36
+
37
+ describe '.user_defined_tag' do
38
+ it 'reads a definition from local file and register it' do
39
+ tag_type_class = AdHocTemplate::Parser::TagType
40
+
41
+ yaml_file_name = 'def_tag.yaml'
42
+ yaml_file_path = File.expand_path(yaml_file_name)
43
+ yaml_source = <<YAML
44
+ ---
45
+ tag_name: :test_tag
46
+ tag: ['<$', '$>']
47
+ iteration_tag: ['<$#', '#$>']
48
+ fallback_tag: ['<$*', '*$>']
49
+ remove_indent: false
50
+ YAML
51
+
52
+ allow(File).to receive(:read).with(yaml_file_path).and_return(yaml_source)
53
+ allow(File).to receive(:expand_path).and_return(yaml_file_path)
54
+
55
+ AdHocTemplate::ConfigManager.user_defined_tag(yaml_file_name)
56
+
57
+ expect(tag_type_class[:test_tag]).to be_instance_of(tag_type_class)
58
+ end
59
+ end
60
+
61
+ describe 'DefaultTagFormatter related methods' do
62
+ before do
63
+ @function_table = AdHocTemplate::DefaultTagFormatter:: FUNCTION_TABLE
64
+ @var = 'french_date'
65
+ @record = { @var => '2016/09/28' }
66
+ @expected_result = '28/09/2016'
67
+ end
68
+
69
+ it '.assign_format_label is an alias of DefaultTagFormatter.assign_format' do
70
+ AdHocTemplate.local_settings do
71
+ assign_format_label('fd') {|var, record| record[var].split(/\//).reverse.join('/') }
72
+ end
73
+
74
+ result = @function_table['fd'].call(@var, @record)
75
+
76
+ expect(result).to eq(@expected_result)
77
+
78
+ @function_table.delete('fd')
79
+ end
80
+
81
+ it '.define_label_format evaluates a given block in the context of DefaultTagFormatter' do
82
+ AdHocTemplate.local_settings do
83
+ define_label_format do
84
+ def french_date(var, record)
85
+ record[var].split(/\//).reverse.join('/')
86
+ end
87
+
88
+ assign_format french_date: 'fd'
89
+ end
90
+ end
91
+
92
+ method_name = @function_table['fd']
93
+ result = AdHocTemplate::DefaultTagFormatter.new.send method_name, @var, @record
94
+
95
+ expect(result).to eq(@expected_result)
96
+
97
+ AdHocTemplate::DefaultTagFormatter.send :undef_method, method_name
98
+ end
99
+ end
100
+
101
+ describe '.init_local_settings' do
102
+ before do
103
+ @config_manager = AdHocTemplate::ConfigManager
104
+ @settings_dir = File.expand_path(@config_manager::LOCAL_SETTINGS_DIR)
105
+ @setting_file_name = @config_manager::SETTINGS_FILE_NAME
106
+ @tag_def_file_name = @config_manager::TAG_DEF_FILE_NAME
107
+ @settings_path = File.expand_path(File.join(@settings_dir, @setting_file_name))
108
+ @settings_file = StringIO.new('', "w")
109
+ @tag_def_path = File.expand_path(File.join(@settings_dir, @tag_def_file_name))
110
+ @tag_def_file = StringIO.new('', "w")
111
+ end
112
+
113
+ it 'creates local setting files unless they exist' do
114
+ allow(File).to receive(:exist?).with(@settings_dir).and_return(false)
115
+ allow(FileUtils).to receive(:mkdir).and_return(true)
116
+ allow(File).to receive(:exist?).with(@settings_path).and_return(false)
117
+ allow(@config_manager).to receive(:open).with(@settings_path, 'w').and_yield(@settings_file)
118
+ allow(File).to receive(:exist?).with(@tag_def_path).and_return(false)
119
+ allow(@config_manager).to receive(:open).with(@tag_def_path, 'w').and_yield(@tag_def_file)
120
+
121
+ @config_manager.init_local_settings
122
+
123
+ expect(@settings_file.string).to start_with('AdHocTemplate')
124
+ expect(@tag_def_file.string).to start_with('---')
125
+ end
126
+
127
+ it 'does nothing if local setting files already exist' do
128
+ allow(File).to receive(:exist?).with(@settings_dir).and_return(true)
129
+ allow(FileUtils).to receive(:mkdir).and_return(false)
130
+ allow(File).to receive(:exist?).with(@settings_path).and_return(true)
131
+ allow(@config_manager).to receive(:open).with(@settings_path, 'w').and_yield(@settings_file)
132
+ allow(File).to receive(:exist?).with(@tag_def_path).and_return(true)
133
+ allow(@config_manager).to receive(:open).with(@tag_def_path, 'w').and_yield(@tag_def_file)
134
+
135
+ @config_manager.init_local_settings
136
+
137
+ expect(@settings_file.string).to be_empty
138
+ expect(@tag_def_file.string).to be_empty
139
+ end
140
+ end
141
+ end
142
+ end
@@ -42,6 +42,19 @@ describe AdHocTemplate do
42
42
  AdHocTemplate::DefaultTagFormatter::FUNCTION_TABLE.delete(proc_label)
43
43
  expect(proc_assigned).to eq('test for proc assignment: <value1>')
44
44
  end
45
+
46
+ it '.assign_format can be used to reassign predefined methods' do
47
+ formatter = AdHocTemplate::DefaultTagFormatter
48
+ default_equal_sign = formatter::FUNCTION_TABLE["="]
49
+ expect(default_equal_sign).to eq(:default)
50
+
51
+ formatter.assign_format(html_encode: "=")
52
+ reassigned_equal_sign = formatter::FUNCTION_TABLE["="]
53
+ expect(reassigned_equal_sign).to eq(:html_encode)
54
+
55
+ formatter.assign_format(default: "=")
56
+ expect(formatter::FUNCTION_TABLE["="]).to eq(default_equal_sign)
57
+ end
45
58
  end
46
59
  end
47
60