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,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