sheng 0.3.2

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 (106) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.gitignore +18 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/.watchr +99 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +62 -0
  10. data/Rakefile +10 -0
  11. data/docs/creating_templates.docx +0 -0
  12. data/docs/creating_templates.md +392 -0
  13. data/lib/sheng/block.rb +59 -0
  14. data/lib/sheng/check_box.rb +43 -0
  15. data/lib/sheng/conditional_block.rb +30 -0
  16. data/lib/sheng/data_set.rb +45 -0
  17. data/lib/sheng/docx.rb +75 -0
  18. data/lib/sheng/merge_field.rb +208 -0
  19. data/lib/sheng/merge_field_set.rb +90 -0
  20. data/lib/sheng/path_helpers.rb +15 -0
  21. data/lib/sheng/sequence.rb +66 -0
  22. data/lib/sheng/support.rb +57 -0
  23. data/lib/sheng/version.rb +3 -0
  24. data/lib/sheng/wml_file.rb +47 -0
  25. data/lib/sheng.rb +21 -0
  26. data/sheng.gemspec +31 -0
  27. data/spec/fixtures/bad_docx_files/with_field_not_in_dataset.docx +0 -0
  28. data/spec/fixtures/bad_docx_files/with_missing_sequence_start.docx +0 -0
  29. data/spec/fixtures/bad_docx_files/with_old_mergefields.docx +0 -0
  30. data/spec/fixtures/bad_docx_files/with_poorly_nested_sequences.docx +0 -0
  31. data/spec/fixtures/bad_docx_files/with_unended_sequence.docx +0 -0
  32. data/spec/fixtures/docx_files/input_document.docx +0 -0
  33. data/spec/fixtures/docx_files/old_style/input_document.docx +0 -0
  34. data/spec/fixtures/docx_files/old_style/output_document.docx +0 -0
  35. data/spec/fixtures/docx_files/output_document.docx +0 -0
  36. data/spec/fixtures/inputs/complete.json +61 -0
  37. data/spec/fixtures/inputs/incomplete.json +52 -0
  38. data/spec/fixtures/trees/embedded_sequence.yml +13 -0
  39. data/spec/fixtures/trees/merge_field_set.yml +16 -0
  40. data/spec/fixtures/xml_fragments/input/check_box/check_box.xml +11 -0
  41. data/spec/fixtures/xml_fragments/input/conditional_block/bad/badly_nested_conditional.xml +105 -0
  42. data/spec/fixtures/xml_fragments/input/conditional_block/bad/unclosed_conditional.xml +35 -0
  43. data/spec/fixtures/xml_fragments/input/conditional_block/conditional_block_if.xml +84 -0
  44. data/spec/fixtures/xml_fragments/input/conditional_block/conditional_block_inline.xml +59 -0
  45. data/spec/fixtures/xml_fragments/input/conditional_block/conditional_block_unless.xml +51 -0
  46. data/spec/fixtures/xml_fragments/input/conditional_block/conditional_in_table.xml +176 -0
  47. data/spec/fixtures/xml_fragments/input/conditional_block/embedded_conditional.xml +79 -0
  48. data/spec/fixtures/xml_fragments/input/merge_field/bad/not_a_real_mergefield_new.xml +19 -0
  49. data/spec/fixtures/xml_fragments/input/merge_field/bad/not_a_real_mergefield_old.xml +9 -0
  50. data/spec/fixtures/xml_fragments/input/merge_field/bad/unclosed_merge_field.xml +25 -0
  51. data/spec/fixtures/xml_fragments/input/merge_field/inline_merge_field.xml +26 -0
  52. data/spec/fixtures/xml_fragments/input/merge_field/merge_field.xml +14 -0
  53. data/spec/fixtures/xml_fragments/input/merge_field/new_merge_field.xml +28 -0
  54. data/spec/fixtures/xml_fragments/input/merge_field/split_merge_field.xml +33 -0
  55. data/spec/fixtures/xml_fragments/input/merge_field_set/complex_nesting_and_reuse.xml +231 -0
  56. data/spec/fixtures/xml_fragments/input/merge_field_set/merge_field_set.xml +89 -0
  57. data/spec/fixtures/xml_fragments/input/merge_field_set/with_non_mergefield_fields.xml +43 -0
  58. data/spec/fixtures/xml_fragments/input/sequence/array_sequence.xml +23 -0
  59. data/spec/fixtures/xml_fragments/input/sequence/bad/badly_nested_sequence.xml +62 -0
  60. data/spec/fixtures/xml_fragments/input/sequence/bad/unclosed_sequence.xml +32 -0
  61. data/spec/fixtures/xml_fragments/input/sequence/embedded_sequence.xml +68 -0
  62. data/spec/fixtures/xml_fragments/input/sequence/inline_sequence.xml +24 -0
  63. data/spec/fixtures/xml_fragments/input/sequence/overridden_iterator_array_sequence.xml +23 -0
  64. data/spec/fixtures/xml_fragments/input/sequence/sequence.xml +39 -0
  65. data/spec/fixtures/xml_fragments/input/sequence/sequence_in_table.xml +125 -0
  66. data/spec/fixtures/xml_fragments/input/sequence/sequence_with_section_formatting.xml +41 -0
  67. data/spec/fixtures/xml_fragments/input/sequence/series_with_commas.xml +73 -0
  68. data/spec/fixtures/xml_fragments/output/check_box/check_box.xml +11 -0
  69. data/spec/fixtures/xml_fragments/output/conditional_block/conditional_in_table_does_not_exist.xml +52 -0
  70. data/spec/fixtures/xml_fragments/output/conditional_block/conditional_in_table_exists.xml +84 -0
  71. data/spec/fixtures/xml_fragments/output/conditional_block/embedded_conditional_both.xml +11 -0
  72. data/spec/fixtures/xml_fragments/output/conditional_block/embedded_conditional_inside.xml +5 -0
  73. data/spec/fixtures/xml_fragments/output/conditional_block/embedded_conditional_outside.xml +8 -0
  74. data/spec/fixtures/xml_fragments/output/conditional_block/if_does_not_exist.xml +5 -0
  75. data/spec/fixtures/xml_fragments/output/conditional_block/if_exists.xml +19 -0
  76. data/spec/fixtures/xml_fragments/output/conditional_block/inline_does_not_exist.xml +10 -0
  77. data/spec/fixtures/xml_fragments/output/conditional_block/inline_exists.xml +13 -0
  78. data/spec/fixtures/xml_fragments/output/conditional_block/unless_does_not_exist.xml +11 -0
  79. data/spec/fixtures/xml_fragments/output/conditional_block/unless_exists.xml +5 -0
  80. data/spec/fixtures/xml_fragments/output/merge_field/inline_merge_field.xml +11 -0
  81. data/spec/fixtures/xml_fragments/output/merge_field/merge_field.xml +12 -0
  82. data/spec/fixtures/xml_fragments/output/merge_field/split_merge_field.xml +10 -0
  83. data/spec/fixtures/xml_fragments/output/merge_field_set/complex_nesting_and_reuse.xml +190 -0
  84. data/spec/fixtures/xml_fragments/output/merge_field_set/merge_field_set.xml +75 -0
  85. data/spec/fixtures/xml_fragments/output/merge_field_set/with_non_mergefield_fields.xml +31 -0
  86. data/spec/fixtures/xml_fragments/output/sequence/array_sequence.xml +17 -0
  87. data/spec/fixtures/xml_fragments/output/sequence/embedded_sequence.xml +56 -0
  88. data/spec/fixtures/xml_fragments/output/sequence/inline_sequence.xml +16 -0
  89. data/spec/fixtures/xml_fragments/output/sequence/overridden_iterator_array_sequence.xml +12 -0
  90. data/spec/fixtures/xml_fragments/output/sequence/sequence.xml +28 -0
  91. data/spec/fixtures/xml_fragments/output/sequence/sequence_in_table.xml +120 -0
  92. data/spec/fixtures/xml_fragments/output/sequence/sequence_with_section_formatting.xml +46 -0
  93. data/spec/fixtures/xml_fragments/output/sequence/series_with_commas.xml +43 -0
  94. data/spec/fixtures/xml_fragments/output/sequence/series_with_commas_two_items.xml +31 -0
  95. data/spec/lib/sheng/check_box_spec.rb +87 -0
  96. data/spec/lib/sheng/conditional_block_spec.rb +151 -0
  97. data/spec/lib/sheng/data_set_spec.rb +65 -0
  98. data/spec/lib/sheng/docx_spec.rb +146 -0
  99. data/spec/lib/sheng/merge_field_set_spec.rb +165 -0
  100. data/spec/lib/sheng/merge_field_spec.rb +276 -0
  101. data/spec/lib/sheng/sequence_spec.rb +201 -0
  102. data/spec/lib/sheng/wml_file_spec.rb +38 -0
  103. data/spec/spec_helper.rb +16 -0
  104. data/spec/support/path_helper.rb +15 -0
  105. data/spec/support/xml_helper.rb +28 -0
  106. metadata +355 -0
@@ -0,0 +1,28 @@
1
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
2
+ <w:p>
3
+ <w:r>
4
+ <w:t>A Radish Summer</w:t>
5
+ </w:r>
6
+ </w:p>
7
+ <w:p>
8
+ <w:r>
9
+ <w:rPr>
10
+ <w:noProof/>
11
+ </w:rPr>
12
+ <w:t>totally rad</w:t>
13
+ </w:r>
14
+ </w:p>
15
+ <w:p>
16
+ <w:r>
17
+ <w:t>Elephants Are Not Your Friend</w:t>
18
+ </w:r>
19
+ </w:p>
20
+ <w:p>
21
+ <w:r>
22
+ <w:rPr>
23
+ <w:noProof/>
24
+ </w:rPr>
25
+ <w:t>stinky</w:t>
26
+ </w:r>
27
+ </w:p>
28
+ </w:document>
@@ -0,0 +1,120 @@
1
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
2
+ <w:tbl>
3
+ <w:tr>
4
+ <w:tc>
5
+ <w:p>
6
+ <w:r>
7
+ <w:t>Meal</w:t>
8
+ </w:r>
9
+ </w:p>
10
+ </w:tc>
11
+ <w:tc>
12
+ <w:p>
13
+ <w:r>
14
+ <w:t>Drink</w:t>
15
+ </w:r>
16
+ </w:p>
17
+ </w:tc>
18
+ <w:tc>
19
+ <w:p>
20
+ <w:r>
21
+ <w:t>Appetizer</w:t>
22
+ </w:r>
23
+ </w:p>
24
+ </w:tc>
25
+ <w:tc>
26
+ <w:p>
27
+ <w:r>
28
+ <w:t>Dessert</w:t>
29
+ </w:r>
30
+ </w:p>
31
+ </w:tc>
32
+ </w:tr>
33
+ <w:tr>
34
+ <w:tc>
35
+ <w:p>
36
+ <w:r>
37
+ <w:t>Gravel Noodles</w:t>
38
+ </w:r>
39
+ </w:p>
40
+ </w:tc>
41
+ <w:tc>
42
+ </w:tc>
43
+ <w:tc>
44
+ <w:p>
45
+ <w:r>
46
+ <w:t>Asphalt Rollups</w:t>
47
+ </w:r>
48
+ </w:p>
49
+ </w:tc>
50
+ <w:tc>
51
+ <w:p>
52
+ <w:r>
53
+ <w:t>Concrete Cream</w:t>
54
+ </w:r>
55
+ </w:p>
56
+ </w:tc>
57
+ </w:tr>
58
+ <w:tr>
59
+ <w:tc>
60
+ <w:p>
61
+ <w:r>
62
+ <w:rPr>
63
+ <w:noProof/>
64
+ </w:rPr>
65
+ <w:t>Steamed Water</w:t>
66
+ </w:r>
67
+ </w:p>
68
+ </w:tc>
69
+ <w:tc>
70
+ </w:tc>
71
+ <w:tc>
72
+ </w:tc>
73
+ <w:tc>
74
+ </w:tc>
75
+ </w:tr>
76
+ <w:tr>
77
+ <w:tc>
78
+ <w:p>
79
+ <w:r>
80
+ <w:t>A Single Olive</w:t>
81
+ </w:r>
82
+ </w:p>
83
+ </w:tc>
84
+ <w:tc>
85
+ </w:tc>
86
+ <w:tc>
87
+ <w:p>
88
+ <w:r>
89
+ <w:t>A Strand of Hair</w:t>
90
+ </w:r>
91
+ </w:p>
92
+ </w:tc>
93
+ <w:tc>
94
+ <w:p>
95
+ <w:r>
96
+ <w:t>Wishes and Hopes</w:t>
97
+ </w:r>
98
+ </w:p>
99
+ </w:tc>
100
+ </w:tr>
101
+ <w:tr>
102
+ <w:tc>
103
+ <w:p>
104
+ <w:r>
105
+ <w:rPr>
106
+ <w:noProof/>
107
+ </w:rPr>
108
+ <w:t>The Memory of Soda</w:t>
109
+ </w:r>
110
+ </w:p>
111
+ </w:tc>
112
+ <w:tc>
113
+ </w:tc>
114
+ <w:tc>
115
+ </w:tc>
116
+ <w:tc>
117
+ </w:tc>
118
+ </w:tr>
119
+ </w:tbl>
120
+ </w:document>
@@ -0,0 +1,46 @@
1
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
2
+ <w:p>
3
+ <w:pPr>
4
+ <w:pStyle w:val="fancy"/>
5
+ <w:sectPr>
6
+ <w:cols w:num="1" w:space="10"/>
7
+ </w:sectPr>
8
+ </w:pPr>
9
+ <w:r>
10
+ <w:t>Tumbiblia</w:t>
11
+ </w:r>
12
+ </w:p>
13
+ <w:p>
14
+ <w:pPr>
15
+ <w:pStyle w:val="fancy"/>
16
+ <w:sectPr>
17
+ <w:cols w:num="2" w:space="20"/>
18
+ </w:sectPr>
19
+ </w:pPr>
20
+ <w:r>
21
+ <w:t>Rofronco</w:t>
22
+ </w:r>
23
+ </w:p>
24
+ <w:p>
25
+ <w:pPr>
26
+ <w:pStyle w:val="fancy"/>
27
+ <w:sectPr>
28
+ <w:cols w:num="2" w:space="20"/>
29
+ </w:sectPr>
30
+ </w:pPr>
31
+ <w:r>
32
+ <w:t>Beanipops</w:t>
33
+ </w:r>
34
+ </w:p>
35
+ <w:p>
36
+ <w:pPr>
37
+ <w:pStyle w:val="dumb"/>
38
+ <w:sectPr>
39
+ <w:cols w:num="3" w:space="30"/>
40
+ </w:sectPr>
41
+ </w:pPr>
42
+ <w:r>
43
+ <w:t>Baab</w:t>
44
+ </w:r>
45
+ </w:p>
46
+ </w:document>
@@ -0,0 +1,43 @@
1
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
2
+ <w:p>
3
+ <w:r>
4
+ <w:t xml:space="preserve">Here's a list of buffoons: </w:t>
5
+ </w:r>
6
+ <w:r>
7
+ <w:t>Snookers</w:t>
8
+ </w:r>
9
+ <w:r>
10
+ <w:t xml:space="preserve"> </w:t>
11
+ </w:r>
12
+ <w:r>
13
+ <w:t>Fumpleton</w:t>
14
+ </w:r>
15
+ <w:r>
16
+ <w:t xml:space="preserve">, </w:t>
17
+ </w:r>
18
+ <w:r>
19
+ <w:t>Francis</w:t>
20
+ </w:r>
21
+ <w:r>
22
+ <w:t xml:space="preserve"> </w:t>
23
+ </w:r>
24
+ <w:r>
25
+ <w:t>Oldgark</w:t>
26
+ </w:r>
27
+ <w:r>
28
+ <w:t xml:space="preserve">, and </w:t>
29
+ </w:r>
30
+ <w:r>
31
+ <w:t>Spanky</w:t>
32
+ </w:r>
33
+ <w:r>
34
+ <w:t xml:space="preserve"> </w:t>
35
+ </w:r>
36
+ <w:r>
37
+ <w:t>McThanks</w:t>
38
+ </w:r>
39
+ <w:r>
40
+ <w:t xml:space="preserve">. Phew. What a list.</w:t>
41
+ </w:r>
42
+ </w:p>
43
+ </w:document>
@@ -0,0 +1,31 @@
1
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
2
+ <w:p>
3
+ <w:r>
4
+ <w:t xml:space="preserve">Here's a list of buffoons: </w:t>
5
+ </w:r>
6
+ <w:r>
7
+ <w:t>Snookers</w:t>
8
+ </w:r>
9
+ <w:r>
10
+ <w:t xml:space="preserve"> </w:t>
11
+ </w:r>
12
+ <w:r>
13
+ <w:t>Fumpleton</w:t>
14
+ </w:r>
15
+ <w:r>
16
+ <w:t xml:space="preserve"> and </w:t>
17
+ </w:r>
18
+ <w:r>
19
+ <w:t>Spanky</w:t>
20
+ </w:r>
21
+ <w:r>
22
+ <w:t xml:space="preserve"> </w:t>
23
+ </w:r>
24
+ <w:r>
25
+ <w:t>McThanks</w:t>
26
+ </w:r>
27
+ <w:r>
28
+ <w:t xml:space="preserve">. Phew. What a list.</w:t>
29
+ </w:r>
30
+ </w:p>
31
+ </w:document>
@@ -0,0 +1,87 @@
1
+ describe Sheng::CheckBox do
2
+ let(:fragment) { xml_fragment('input/check_box/check_box') }
3
+ let(:element) { fragment.at_xpath(".//w:checkBox/..") }
4
+ subject { described_class.new(element) }
5
+
6
+ describe ".from_element" do
7
+ it "returns a new instance with the given element" do
8
+ expect(described_class.from_element(element)).to eq(subject)
9
+ end
10
+ end
11
+
12
+ describe '#key' do
13
+ it 'returns w:val attribute of w:name node in xml fragment' do
14
+ expect(subject.key).to eq 'goats'
15
+ end
16
+ end
17
+
18
+ describe '#raw_key' do
19
+ it 'returns key' do
20
+ expect(subject.raw_key).to eq subject.key
21
+ end
22
+ end
23
+
24
+ describe '#value_is_truthy?' do
25
+ context 'given truthy values' do
26
+ ['true', 'TRuE', true, 1, '1', 'yes'].each do |truthy|
27
+ given_key = truthy.is_a?(String) ? "'#{truthy}' (as string)" : truthy
28
+ it "returns true if key is #{given_key} in dataset" do
29
+ expect(subject.value_is_truthy?(truthy)).to be_truthy
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'given falsy values' do
35
+ ['false', 'FaLSE', false, 0, '0', 'no', '', nil].each do |falsy|
36
+ given_key = falsy.is_a?(String) ? "'#{falsy}' (as string)" : falsy
37
+ given_key = 'nil' if falsy.nil?
38
+ it "returns false if key is #{given_key} in dataset" do
39
+ expect(subject.value_is_truthy?(falsy)).to be_falsy
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#interpolate' do
46
+ let(:dataset) { Sheng::DataSet.new('goats' => 'foofle') }
47
+
48
+ it "checks the checkbox if key is truthy in dataset" do
49
+ allow(subject).to receive(:value_is_truthy?).with('foofle').and_return(true)
50
+ subject.interpolate(dataset)
51
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/check_box/check_box')
52
+ end
53
+
54
+ it "does not check the checkbox if key is falsy in dataset" do
55
+ allow(subject).to receive(:value_is_truthy?).with('foofle').and_return(false)
56
+ subject.interpolate(dataset)
57
+ expect(subject.xml_document).to be_equivalent_to fragment_with_unchecked_box(subject.xml_document)
58
+ end
59
+
60
+ it "does not uncheck a default checked checkbox if key is truthy in dataset" do
61
+ default_checked = described_class.new(fragment_with_checked_box(subject.xml_document))
62
+ allow(default_checked).to receive(:value_is_truthy?).with('foofle').and_return(true)
63
+ default_checked.interpolate(dataset)
64
+ expect(default_checked.xml_document).to be_equivalent_to xml_fragment('output/check_box/check_box')
65
+ end
66
+
67
+ it "unchecks a default checked checkbox if key is falsy in dataset" do
68
+ default_checked = described_class.new(fragment_with_checked_box(subject.xml_document))
69
+ allow(default_checked).to receive(:value_is_truthy?).with('foofle').and_return(false)
70
+ default_checked.interpolate(dataset)
71
+ expect(default_checked.xml_document).to be_equivalent_to fragment_with_unchecked_box(default_checked.xml_document)
72
+ end
73
+
74
+ it "does not check the checkbox if key is not found in dataset" do
75
+ dataset = Sheng::DataSet.new({})
76
+ subject.interpolate(dataset)
77
+ expect(subject.xml_document).to be_equivalent_to fragment_with_unchecked_box(subject.xml_document)
78
+ end
79
+
80
+ it "does not uncheck a checked checkbox if key is not found in dataset" do
81
+ dataset = Sheng::DataSet.new({})
82
+ default_checked = described_class.new(fragment_with_checked_box(subject.xml_document))
83
+ default_checked.interpolate(dataset)
84
+ expect(default_checked.xml_document).to be_equivalent_to xml_fragment('output/check_box/check_box')
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,151 @@
1
+ describe Sheng::ConditionalBlock do
2
+ let(:fragment) { xml_fragment('input/conditional_block/conditional_block_if') }
3
+ let(:element) { fragment.xpath("//w:fldChar[contains(@w:fldCharType, 'begin')]").first }
4
+ let(:merge_field) { Sheng::MergeField.new(element) }
5
+ subject { described_class.new(merge_field) }
6
+
7
+ describe "#raw_key" do
8
+ it "returns key from start_field" do
9
+ expect(subject.raw_key).to eq(merge_field.raw_key)
10
+ end
11
+ end
12
+
13
+ describe '#interpolate' do
14
+ let(:fragment) { xml_fragment('input/conditional_block/conditional_block_inline') }
15
+
16
+ context "inline" do
17
+ it 'includes if sections when variable truthy' do
18
+ dataset = Sheng::DataSet.new({
19
+ :alicorns => 'so tasties'
20
+ })
21
+
22
+ subject.interpolate(dataset)
23
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/inline_exists')
24
+ end
25
+
26
+ it 'removes entire section if variable falsy' do
27
+ dataset = Sheng::DataSet.new({})
28
+
29
+ subject.interpolate(dataset)
30
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/inline_does_not_exist')
31
+ end
32
+ end
33
+
34
+ context "with if block" do
35
+ let(:fragment) { xml_fragment('input/conditional_block/conditional_block_if') }
36
+
37
+ it 'includes sections when variable truthy' do
38
+ dataset = Sheng::DataSet.new({
39
+ :alicorns => 'so tasties'
40
+ })
41
+
42
+ subject.interpolate(dataset)
43
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/if_exists')
44
+ end
45
+
46
+ it 'removes entire section if variable falsy' do
47
+ dataset = Sheng::DataSet.new({})
48
+
49
+ subject.interpolate(dataset)
50
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/if_does_not_exist')
51
+ end
52
+ end
53
+
54
+ context "with unless block" do
55
+ let(:fragment) { xml_fragment('input/conditional_block/conditional_block_unless') }
56
+
57
+ it 'includes sections when variable falsy' do
58
+ dataset = Sheng::DataSet.new({})
59
+
60
+ subject.interpolate(dataset)
61
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/unless_does_not_exist')
62
+ end
63
+
64
+ it 'removes entire section if variable truthy' do
65
+ dataset = Sheng::DataSet.new({
66
+ :alicorns => 'so tasties'
67
+ })
68
+
69
+ subject.interpolate(dataset)
70
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/unless_exists')
71
+ end
72
+ end
73
+
74
+ context "within a table" do
75
+ let(:fragment) { xml_fragment('input/conditional_block/conditional_in_table') }
76
+ let(:dataset_hash) {
77
+ {
78
+ :include_dogs => true,
79
+ :dog_favorites => [
80
+ { :name => "Fluffy the Android", :megaphone => "YellTheBest" },
81
+ { :name => "Derek Dog", :megaphone => "Super Primo Loudkins" }
82
+ ]
83
+ }
84
+ }
85
+
86
+ it "includes if rows when variable truthy" do
87
+ dataset = Sheng::DataSet.new(dataset_hash)
88
+
89
+ subject.interpolate(dataset)
90
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/conditional_in_table_exists')
91
+ end
92
+
93
+ it "removes entire section when variable falsy" do
94
+ dataset = Sheng::DataSet.new(dataset_hash.merge(:include_dogs => false))
95
+
96
+ subject.interpolate(dataset)
97
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/conditional_in_table_does_not_exist')
98
+ end
99
+ end
100
+
101
+ context "with an embedded conditional" do
102
+ let(:fragment) { xml_fragment('input/conditional_block/embedded_conditional') }
103
+ let(:dataset_hash) {
104
+ { :alicorns => true, :scubas => true }
105
+ }
106
+
107
+ it "includes both sections when both are truthy" do
108
+ dataset = Sheng::DataSet.new(dataset_hash)
109
+
110
+ subject.interpolate(dataset)
111
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/embedded_conditional_both')
112
+ end
113
+
114
+ it "includes only outside section if inside falsy" do
115
+ dataset = Sheng::DataSet.new(dataset_hash.merge(:scubas => false))
116
+
117
+ subject.interpolate(dataset)
118
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/embedded_conditional_outside')
119
+ end
120
+
121
+ it "ignores truthy inside section if outside section is falsy" do
122
+ dataset = Sheng::DataSet.new(dataset_hash.merge(:alicorns => false))
123
+
124
+ subject.interpolate(dataset)
125
+ expect(subject.xml_document).to be_equivalent_to xml_fragment('output/conditional_block/embedded_conditional_inside')
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#new' do
131
+ context "with unclosed conditional" do
132
+ let(:fragment) { xml_fragment('input/conditional_block/bad/unclosed_conditional') }
133
+ it 'raises an exception indicating the missing end tag' do
134
+ dataset = Sheng::DataSet.new({})
135
+ expect {
136
+ subject
137
+ }.to raise_error(described_class::MissingEndTag, "no end tag for if:alicorns")
138
+ end
139
+ end
140
+
141
+ context "with badly nested conditional" do
142
+ let(:fragment) { xml_fragment('input/conditional_block/bad/badly_nested_conditional') }
143
+ it 'raises an exception indicating the nesting issue' do
144
+ dataset = Sheng::DataSet.new({})
145
+ expect {
146
+ subject
147
+ }.to raise_error(described_class::ImproperNesting, "expected end tag for unless:pumpkins, got end_if:alicorns")
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,65 @@
1
+ describe Sheng::DataSet do
2
+ describe '#new' do
3
+ it 'raises an argument error if not given a Hash' do
4
+ not_a_hash = double('Not a Hash')
5
+ allow(not_a_hash).to receive(:is_a?).with(Hash).and_return(false)
6
+ expect {
7
+ described_class.new(not_a_hash)
8
+ }.to raise_error(ArgumentError, "must be initialized with a Hash")
9
+ end
10
+
11
+ it 'deep symbolizes the given hash and assigns it to #raw_hash' do
12
+ a_hash = double(Hash, :deep_symbolize_keys => :an_awesome_symbolized_hash)
13
+ allow(a_hash).to receive(:is_a?).with(Hash).and_return(true)
14
+ subject = described_class.new(a_hash)
15
+ expect(subject.raw_hash).to eq :an_awesome_symbolized_hash
16
+ end
17
+ end
18
+
19
+ describe '#fetch' do
20
+ let(:raw_hash) {
21
+ {
22
+ :rabbits => {
23
+ :streamlined => [:super_rabbit, :dr_slinky],
24
+ :slow => :mr_molasses
25
+ }
26
+ }
27
+ }
28
+ subject { described_class.new(raw_hash) }
29
+
30
+ it 'returns a non-hash object found at key' do
31
+ expect(subject.fetch('rabbits.slow')).to eq :mr_molasses
32
+ expect(subject.fetch('rabbits.streamlined')).to eq [:super_rabbit, :dr_slinky]
33
+ end
34
+
35
+ it 'raises an error if not given a string' do
36
+ not_a_string = double('Not a String')
37
+ allow(not_a_string).to receive(:is_a?).with(String).and_return(false)
38
+ expect {
39
+ subject.fetch(not_a_string)
40
+ }.to raise_error(ArgumentError, "must provide a string")
41
+ end
42
+
43
+ it 'raises a KeyNotFound error if key not found' do
44
+ expect {
45
+ subject.fetch('rabbits.funky')
46
+ }.to raise_error(described_class::KeyNotFound, "did not find in dataset: rabbits.funky (funky not found)")
47
+ end
48
+
49
+ it 'does not raise error on key not found if default given' do
50
+ expect(subject.fetch('rabbits.funky', :default => "horse")).to eq("horse")
51
+ end
52
+
53
+ it 'raises an error if Hash found at key' do
54
+ expect {
55
+ subject.fetch('rabbits')
56
+ }.to raise_error(described_class::KeyNotFound, "result at rabbits is a Hash")
57
+ end
58
+
59
+ it 'raises an error if key is too long' do
60
+ expect {
61
+ subject.fetch('rabbits.slow.but_steady')
62
+ }.to raise_error(described_class::KeyNotFound, "in rabbits.slow.but_steady, slow did not return a Hash")
63
+ end
64
+ end
65
+ end