berkeley_library-tind 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 (162) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +388 -0
  4. data/.idea/inspectionProfiles/Project_Default.xml +20 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/tind.iml +138 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.rubocop.yml +334 -0
  10. data/.ruby-version +1 -0
  11. data/.simplecov +8 -0
  12. data/.yardopts +1 -0
  13. data/CHANGES.md +58 -0
  14. data/Dockerfile +57 -0
  15. data/Gemfile +3 -0
  16. data/Jenkinsfile +18 -0
  17. data/LICENSE.md +21 -0
  18. data/README.md +73 -0
  19. data/Rakefile +20 -0
  20. data/berkeley_library-tind.gemspec +50 -0
  21. data/bin/tind-export +14 -0
  22. data/docker-compose.yml +15 -0
  23. data/lib/berkeley_library/tind.rb +3 -0
  24. data/lib/berkeley_library/tind/api.rb +1 -0
  25. data/lib/berkeley_library/tind/api/api.rb +132 -0
  26. data/lib/berkeley_library/tind/api/api_exception.rb +131 -0
  27. data/lib/berkeley_library/tind/api/collection.rb +82 -0
  28. data/lib/berkeley_library/tind/api/date_range.rb +67 -0
  29. data/lib/berkeley_library/tind/api/format.rb +32 -0
  30. data/lib/berkeley_library/tind/api/search.rb +100 -0
  31. data/lib/berkeley_library/tind/config.rb +103 -0
  32. data/lib/berkeley_library/tind/export.rb +1 -0
  33. data/lib/berkeley_library/tind/export/column.rb +54 -0
  34. data/lib/berkeley_library/tind/export/column_group.rb +144 -0
  35. data/lib/berkeley_library/tind/export/column_group_list.rb +131 -0
  36. data/lib/berkeley_library/tind/export/column_width_calculator.rb +76 -0
  37. data/lib/berkeley_library/tind/export/config.rb +154 -0
  38. data/lib/berkeley_library/tind/export/csv_exporter.rb +29 -0
  39. data/lib/berkeley_library/tind/export/export.rb +47 -0
  40. data/lib/berkeley_library/tind/export/export_command.rb +168 -0
  41. data/lib/berkeley_library/tind/export/export_exception.rb +8 -0
  42. data/lib/berkeley_library/tind/export/export_format.rb +67 -0
  43. data/lib/berkeley_library/tind/export/exporter.rb +105 -0
  44. data/lib/berkeley_library/tind/export/filter.rb +52 -0
  45. data/lib/berkeley_library/tind/export/no_results_error.rb +7 -0
  46. data/lib/berkeley_library/tind/export/ods_exporter.rb +138 -0
  47. data/lib/berkeley_library/tind/export/row.rb +24 -0
  48. data/lib/berkeley_library/tind/export/row_metrics.rb +18 -0
  49. data/lib/berkeley_library/tind/export/table.rb +175 -0
  50. data/lib/berkeley_library/tind/export/table_metrics.rb +116 -0
  51. data/lib/berkeley_library/tind/marc.rb +1 -0
  52. data/lib/berkeley_library/tind/marc/xml_reader.rb +144 -0
  53. data/lib/berkeley_library/tind/module_info.rb +14 -0
  54. data/lib/berkeley_library/util/arrays.rb +178 -0
  55. data/lib/berkeley_library/util/logging.rb +1 -0
  56. data/lib/berkeley_library/util/ods/spreadsheet.rb +170 -0
  57. data/lib/berkeley_library/util/ods/xml/content_doc.rb +26 -0
  58. data/lib/berkeley_library/util/ods/xml/document_node.rb +57 -0
  59. data/lib/berkeley_library/util/ods/xml/element_node.rb +106 -0
  60. data/lib/berkeley_library/util/ods/xml/loext/table_protection.rb +26 -0
  61. data/lib/berkeley_library/util/ods/xml/manifest/file_entry.rb +42 -0
  62. data/lib/berkeley_library/util/ods/xml/manifest/manifest.rb +73 -0
  63. data/lib/berkeley_library/util/ods/xml/manifest_doc.rb +26 -0
  64. data/lib/berkeley_library/util/ods/xml/namespace.rb +46 -0
  65. data/lib/berkeley_library/util/ods/xml/office/automatic_styles.rb +181 -0
  66. data/lib/berkeley_library/util/ods/xml/office/body.rb +17 -0
  67. data/lib/berkeley_library/util/ods/xml/office/document_content.rb +98 -0
  68. data/lib/berkeley_library/util/ods/xml/office/document_styles.rb +39 -0
  69. data/lib/berkeley_library/util/ods/xml/office/font_face_decls.rb +30 -0
  70. data/lib/berkeley_library/util/ods/xml/office/scripts.rb +17 -0
  71. data/lib/berkeley_library/util/ods/xml/office/spreadsheet.rb +37 -0
  72. data/lib/berkeley_library/util/ods/xml/office/styles.rb +39 -0
  73. data/lib/berkeley_library/util/ods/xml/style/cell_style.rb +58 -0
  74. data/lib/berkeley_library/util/ods/xml/style/column_style.rb +36 -0
  75. data/lib/berkeley_library/util/ods/xml/style/default_style.rb +31 -0
  76. data/lib/berkeley_library/util/ods/xml/style/family.rb +85 -0
  77. data/lib/berkeley_library/util/ods/xml/style/font_face.rb +46 -0
  78. data/lib/berkeley_library/util/ods/xml/style/paragraph_properties.rb +30 -0
  79. data/lib/berkeley_library/util/ods/xml/style/row_style.rb +37 -0
  80. data/lib/berkeley_library/util/ods/xml/style/style.rb +44 -0
  81. data/lib/berkeley_library/util/ods/xml/style/table_cell_properties.rb +40 -0
  82. data/lib/berkeley_library/util/ods/xml/style/table_column_properties.rb +30 -0
  83. data/lib/berkeley_library/util/ods/xml/style/table_properties.rb +25 -0
  84. data/lib/berkeley_library/util/ods/xml/style/table_row_properties.rb +28 -0
  85. data/lib/berkeley_library/util/ods/xml/style/table_style.rb +27 -0
  86. data/lib/berkeley_library/util/ods/xml/style/text_properties.rb +52 -0
  87. data/lib/berkeley_library/util/ods/xml/styles_doc.rb +26 -0
  88. data/lib/berkeley_library/util/ods/xml/table/named_expressions.rb +17 -0
  89. data/lib/berkeley_library/util/ods/xml/table/repeatable.rb +38 -0
  90. data/lib/berkeley_library/util/ods/xml/table/table.rb +193 -0
  91. data/lib/berkeley_library/util/ods/xml/table/table_cell.rb +46 -0
  92. data/lib/berkeley_library/util/ods/xml/table/table_column.rb +43 -0
  93. data/lib/berkeley_library/util/ods/xml/table/table_row.rb +136 -0
  94. data/lib/berkeley_library/util/ods/xml/text/p.rb +118 -0
  95. data/lib/berkeley_library/util/paths.rb +111 -0
  96. data/lib/berkeley_library/util/stringios.rb +30 -0
  97. data/lib/berkeley_library/util/strings.rb +42 -0
  98. data/lib/berkeley_library/util/sys_exits.rb +15 -0
  99. data/lib/berkeley_library/util/times.rb +22 -0
  100. data/lib/berkeley_library/util/uris.rb +44 -0
  101. data/lib/berkeley_library/util/uris/appender.rb +162 -0
  102. data/lib/berkeley_library/util/uris/requester.rb +62 -0
  103. data/lib/berkeley_library/util/uris/validator.rb +32 -0
  104. data/rakelib/bundle.rake +8 -0
  105. data/rakelib/coverage.rake +11 -0
  106. data/rakelib/gem.rake +54 -0
  107. data/rakelib/rubocop.rake +18 -0
  108. data/rakelib/spec.rake +2 -0
  109. data/spec/.rubocop.yml +40 -0
  110. data/spec/berkeley_library/tind/api/api_exception_spec.rb +91 -0
  111. data/spec/berkeley_library/tind/api/api_spec.rb +143 -0
  112. data/spec/berkeley_library/tind/api/collection_spec.rb +74 -0
  113. data/spec/berkeley_library/tind/api/date_range_spec.rb +110 -0
  114. data/spec/berkeley_library/tind/api/format_spec.rb +54 -0
  115. data/spec/berkeley_library/tind/api/search_spec.rb +364 -0
  116. data/spec/berkeley_library/tind/config_spec.rb +86 -0
  117. data/spec/berkeley_library/tind/export/column_group_spec.rb +29 -0
  118. data/spec/berkeley_library/tind/export/column_spec.rb +43 -0
  119. data/spec/berkeley_library/tind/export/config_spec.rb +206 -0
  120. data/spec/berkeley_library/tind/export/export_command_spec.rb +169 -0
  121. data/spec/berkeley_library/tind/export/export_format_spec.rb +59 -0
  122. data/spec/berkeley_library/tind/export/export_matcher.rb +112 -0
  123. data/spec/berkeley_library/tind/export/export_spec.rb +150 -0
  124. data/spec/berkeley_library/tind/export/exporter_spec.rb +125 -0
  125. data/spec/berkeley_library/tind/export/row_spec.rb +118 -0
  126. data/spec/berkeley_library/tind/export/table_spec.rb +322 -0
  127. data/spec/berkeley_library/tind/marc/xml_reader_spec.rb +93 -0
  128. data/spec/berkeley_library/util/arrays_spec.rb +340 -0
  129. data/spec/berkeley_library/util/ods/spreadsheet_spec.rb +124 -0
  130. data/spec/berkeley_library/util/ods/xml/content_doc_spec.rb +121 -0
  131. data/spec/berkeley_library/util/ods/xml/manifest/file_entry_spec.rb +27 -0
  132. data/spec/berkeley_library/util/ods/xml/manifest/manifest_spec.rb +33 -0
  133. data/spec/berkeley_library/util/ods/xml/office/document_content_spec.rb +60 -0
  134. data/spec/berkeley_library/util/ods/xml/style/automatic_styles_spec.rb +37 -0
  135. data/spec/berkeley_library/util/ods/xml/style/family_spec.rb +57 -0
  136. data/spec/berkeley_library/util/ods/xml/table/table_row_spec.rb +179 -0
  137. data/spec/berkeley_library/util/ods/xml/table/table_spec.rb +218 -0
  138. data/spec/berkeley_library/util/paths_spec.rb +90 -0
  139. data/spec/berkeley_library/util/stringios_spec.rb +34 -0
  140. data/spec/berkeley_library/util/strings_spec.rb +27 -0
  141. data/spec/berkeley_library/util/times_spec.rb +39 -0
  142. data/spec/berkeley_library/util/uris_spec.rb +118 -0
  143. data/spec/data/collection-names.txt +438 -0
  144. data/spec/data/collections.json +4827 -0
  145. data/spec/data/disjoint-records.xml +187 -0
  146. data/spec/data/record-184453.xml +58 -0
  147. data/spec/data/record-184458.xml +63 -0
  148. data/spec/data/record-187888.xml +78 -0
  149. data/spec/data/records-api-search-cjk-p1.xml +6381 -0
  150. data/spec/data/records-api-search-cjk-p2.xml +5 -0
  151. data/spec/data/records-api-search-p1.xml +4506 -0
  152. data/spec/data/records-api-search-p2.xml +4509 -0
  153. data/spec/data/records-api-search-p3.xml +4506 -0
  154. data/spec/data/records-api-search-p4.xml +4509 -0
  155. data/spec/data/records-api-search-p5.xml +4506 -0
  156. data/spec/data/records-api-search-p6.xml +2436 -0
  157. data/spec/data/records-api-search-p7.xml +5 -0
  158. data/spec/data/records-api-search.xml +234 -0
  159. data/spec/data/records-manual-search.xml +547 -0
  160. data/spec/spec_helper.rb +30 -0
  161. data/test/profile/table_from_records_profile.rb +46 -0
  162. metadata +585 -0
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rexml/document'
4
+
5
+ module BerkeleyLibrary
6
+ module Util
7
+ module ODS
8
+ module XML
9
+ describe ContentDoc do
10
+ let(:content) { ContentDoc.new }
11
+
12
+ describe :document_content do
13
+ it 'returns a DocumentContent' do
14
+ expect(content.document_content).to be_a(Office::DocumentContent)
15
+ end
16
+ end
17
+
18
+ describe :to_xml do
19
+ attr_reader :doc, :root
20
+
21
+ before(:each) do
22
+ xml_str = content.to_xml
23
+ @doc = REXML::Document.new(xml_str)
24
+ @root = doc.root
25
+ end
26
+
27
+ describe :root do
28
+
29
+ it 'is a <document-content/> element' do
30
+ expect(root).to be_a(REXML::Element)
31
+ expect(root.prefix).to eq('office')
32
+ expect(root.name).to eq('document-content')
33
+ expect(root.namespace).to eq('urn:oasis:names:tc:opendocument:xmlns:office:1.0')
34
+ end
35
+
36
+ it 'includes all namespaces' do
37
+ aggregate_failures('namespaces') do
38
+ Office::DocumentContent::REQUIRED_NAMESPACES.each do |ns|
39
+ expect(root["xmlns:#{ns.prefix}"]).to eq(ns.uri)
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '<scripts/>' do
45
+ it 'includes a <scripts/> element' do
46
+ elem = root.elements[1]
47
+ expect(elem).to be_a(REXML::Element)
48
+ expect(elem.prefix).to eq('office')
49
+ expect(elem.name).to eq('scripts')
50
+ end
51
+ end
52
+
53
+ describe '<font-face-decls/>' do
54
+ attr_reader :decls
55
+
56
+ before(:each) do
57
+ @decls = root.elements[2]
58
+ end
59
+
60
+ it 'includes a <font-face-decls/> element' do
61
+ expect(decls).to be_a(REXML::Element)
62
+ expect(decls.prefix).to eq('office')
63
+ expect(decls.name).to eq('font-face-decls')
64
+ end
65
+
66
+ it 'includes the default font face' do
67
+ face = decls.elements[1]
68
+ expect(face).to be_a(REXML::Element)
69
+ expect(face.prefix).to eq('style')
70
+ expect(face.name).to eq('font-face')
71
+
72
+ expected_face = Style::FontFace::DEFAULT_FONT_FACE
73
+
74
+ expect(face['style:name']).to eq(expected_face)
75
+ expect(face['svg:font-family']).to eq("'#{expected_face}'")
76
+ expect(face['style:font-family-generic']).to eq('swiss')
77
+ end
78
+ end
79
+
80
+ describe '<automatic-styles/>' do
81
+ attr_reader :styles
82
+
83
+ before(:each) do
84
+ @styles = root.elements[3]
85
+ end
86
+
87
+ it 'includes an <automatic-styles/> element' do
88
+ expect(styles).to be_a(REXML::Element)
89
+ expect(styles.prefix).to eq('office')
90
+ expect(styles.name).to eq('automatic-styles')
91
+ end
92
+ end
93
+
94
+ describe '<body/>' do
95
+ attr_reader :body
96
+
97
+ before(:each) do
98
+ @body = root.elements[4]
99
+ end
100
+
101
+ it 'includes a <body/> element' do
102
+ expect(body).to be_a(REXML::Element)
103
+ expect(body.prefix).to eq('office')
104
+ expect(body.name).to eq('body')
105
+ end
106
+
107
+ it 'includes a <spreadsheet/> element' do
108
+ spreadsheet = body.elements[1]
109
+ expect(spreadsheet).to be_a(REXML::Element)
110
+ expect(spreadsheet.prefix).to eq('office')
111
+ expect(spreadsheet.name).to eq('spreadsheet')
112
+ end
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Manifest
8
+ describe FileEntry do
9
+ let(:doc) { Nokogiri::XML::Document.new }
10
+ let(:manifest) { Manifest.new(doc: doc) }
11
+
12
+ describe :new do
13
+ it 'guesses the media type' do
14
+ entry = FileEntry.new('foo.xml', manifest: manifest)
15
+ expect(entry.media_type).to eq('text/xml')
16
+ end
17
+
18
+ it 'rejects unknown types' do
19
+ expect { FileEntry.new('foo.bar', manifest: manifest) }.to raise_error(ArgumentError)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Manifest
8
+ describe Manifest do
9
+ let(:doc) { Nokogiri::XML::Document.new }
10
+
11
+ attr_reader :manifest
12
+
13
+ before(:each) do
14
+ @manifest = Manifest.new(doc: doc)
15
+ end
16
+
17
+ describe :add_child do
18
+ it 'adds a FileEntry' do
19
+ entry = FileEntry.new('foo.bar', 'foo/bar', manifest: manifest)
20
+ manifest.add_child(entry)
21
+ end
22
+
23
+ it 'adds a non-FileEntry' do
24
+ child = ElementNode.new(:office, 'test', doc: doc)
25
+ manifest.add_child(child)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Office
8
+ describe DocumentContent do
9
+ let(:content) { ContentDoc.new }
10
+ let(:doc_content) { content.document_content }
11
+
12
+ # TODO: move to some kind of helper & share w/table_spec
13
+ def doc_content_xml
14
+ xml_str = content.to_xml
15
+ # File.open("tmp/content-#{Time.now.to_i}.xml", 'wb') { |f| f.write(xml_str) }
16
+ rexml_doc = REXML::Document.new(xml_str)
17
+ rexml_doc.root
18
+ end
19
+
20
+ # TODO: move to some kind of helper & share w/table_spec
21
+ def find_table_xml(dc_xml = doc_content_xml, table_index = 1)
22
+ body = dc_xml.elements[4]
23
+ expect(body).to be_a(REXML::Element) # just to be sure
24
+
25
+ spreadsheet = body.elements[1]
26
+ expect(spreadsheet).to be_a(REXML::Element) # just to be sure
27
+
28
+ spreadsheet.elements[table_index]
29
+ end
30
+
31
+ describe :add_table do
32
+
33
+ let(:table_name) { "Table for #{File.basename(__FILE__, '.rb')}" }
34
+
35
+ attr_reader :table
36
+
37
+ before(:each) do
38
+ @table = doc_content.add_table(table_name)
39
+ end
40
+
41
+ it 'creates a table' do
42
+ expect(table).to be_a(XML::Table::Table)
43
+ end
44
+
45
+ it 'adds the table to the XML' do
46
+ table_xml = find_table_xml
47
+ expect(table_xml).to be_a(REXML::Element)
48
+ expect(table_xml.prefix).to eq('table')
49
+ expect(table_xml.name).to eq('table')
50
+
51
+ expect(table_xml['table:name']).to eq(table_name)
52
+ expect(table_xml['table:protected']).to eq('true')
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Office
8
+ describe AutomaticStyles do
9
+ let(:doc) { Nokogiri::XML::Document.new }
10
+
11
+ attr_reader :styles
12
+
13
+ before(:each) do
14
+ @styles = AutomaticStyles.new(doc: doc)
15
+ end
16
+
17
+ describe :add_child do
18
+ it 'adds a style' do
19
+ protected = false
20
+ color = '#abcdef'
21
+
22
+ style = Style::CellStyle.new('my-style', protected, color, styles: styles)
23
+ styles.add_child(style)
24
+ expect(styles.find_cell_style(protected, color: color)).to be(style)
25
+ end
26
+
27
+ it 'adds a non-style' do
28
+ non_style = XML::Text::P.new('abcdef', doc: doc)
29
+ styles.add_child(non_style)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Style
8
+ describe Family do
9
+ describe :ensure_family do
10
+ it "returns a #{Family} as itself" do
11
+ Family.each do |f|
12
+ expect(Family.ensure_family(f)).to be(f)
13
+ end
14
+ end
15
+
16
+ it 'finds a family by key' do
17
+ Family.each do |f|
18
+ variants = [
19
+ f.key.to_s,
20
+ f.key.to_sym,
21
+ f.key.to_s.upcase,
22
+ f.key.to_s.upcase.to_sym,
23
+ f.key.to_s.downcase,
24
+ f.key.to_s.downcase.to_sym
25
+ ]
26
+ variants.each do |k|
27
+ expect(Family.ensure_family(k)).to be(f)
28
+ end
29
+ end
30
+ end
31
+
32
+ it 'finds a family by value' do
33
+ Family.each do |f|
34
+ variants = [
35
+ f.value.to_s,
36
+ f.value.to_sym,
37
+ f.value.to_s.upcase,
38
+ f.value.to_s.upcase.to_sym,
39
+ f.value.to_s.downcase,
40
+ f.value.to_s.downcase.to_sym
41
+ ]
42
+ variants.each do |v|
43
+ expect(Family.ensure_family(v)).to be(f)
44
+ end
45
+ end
46
+ end
47
+
48
+ it 'rejects invalid families' do
49
+ expect { Family.ensure_family(:not_a_family) }.to raise_error(ArgumentError)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ module BerkeleyLibrary
4
+ module Util
5
+ module ODS
6
+ module XML
7
+ module Table
8
+ describe TableRow do
9
+ let(:content) { ContentDoc.new }
10
+ let(:doc_content) { content.document_content }
11
+ let(:table_name) { "Table for #{File.basename(__FILE__, '.rb')}" }
12
+
13
+ attr_reader :table, :row
14
+
15
+ before(:each) do
16
+ @table = doc_content.add_table(table_name)
17
+ table.add_column('Column 1')
18
+ @row = table.add_row
19
+ end
20
+
21
+ # TODO: move to some kind of helper & share w/table_spec
22
+ def doc_content_xml
23
+ xml_str = content.to_xml
24
+ # File.open("tmp/content-#{Time.now.to_i}.xml", 'wb') { |f| f.write(xml_str) }
25
+ rexml_doc = REXML::Document.new(xml_str)
26
+ rexml_doc.root
27
+ end
28
+
29
+ # TODO: move to some kind of helper & share w/table_spec
30
+ def find_table_xml(dc_xml = doc_content_xml, table_index = 1)
31
+ body = dc_xml.elements[4]
32
+ expect(body).to be_a(REXML::Element) # just to be sure
33
+
34
+ spreadsheet = body.elements[1]
35
+ expect(spreadsheet).to be_a(REXML::Element) # just to be sure
36
+
37
+ spreadsheet.elements[table_index]
38
+ end
39
+
40
+ def find_row_xml(table_xml = find_table_xml, row_index = 2)
41
+ table_xml.elements[row_index, 'table-row']
42
+ end
43
+
44
+ def find_cell_xml(table_xml = find_table_xml, row_index = 2, col_index = 0)
45
+ row_xml = table_xml.elements[row_index, 'table-row']
46
+ cells_xml = row_xml.elements.select { |e| e.name == 'table-cell' }
47
+ cells_xml[col_index]
48
+ end
49
+
50
+ describe :set_value_at do
51
+ it 'sets a value' do
52
+ value = 'Value 0'
53
+ row.set_value_at(0, value)
54
+ expect(table.get_value_at(1, 0)).to eq(value)
55
+
56
+ row_xml = find_row_xml
57
+ cells_xml = row_xml.elements.select { |e| e.name == 'table-cell' }
58
+ expect(cells_xml.size).to eq(2)
59
+
60
+ cell_xml = cells_xml[0]
61
+ p = cell_xml.elements[1, 'p'] # row 1 is header
62
+ expect(p).to be_a(REXML::Element)
63
+ expect(p.prefix).to eq('text')
64
+
65
+ texts = p.texts
66
+ expect(texts.size).to eq(1)
67
+ expect(texts[0].value).to eq(value)
68
+
69
+ dummy_cell_xml = cells_xml[1]
70
+ expected_dummy_cols = Table::MIN_COLUMNS - 1
71
+ expect(dummy_cell_xml['table:number-columns-repeated']).to eq(expected_dummy_cols.to_s)
72
+ end
73
+
74
+ it 'escapes tabs, newlines, and runs of spaces' do
75
+ value_fragments = [
76
+ ' test test ', "\t", 'test ', ' ', ' ', 'test test', "\n", 'test test ', ' '
77
+ ]
78
+ value = value_fragments.join
79
+ row.set_value_at(0, value)
80
+ expect(table.get_value_at(1, 0)).to eq(value)
81
+
82
+ row_xml = find_row_xml
83
+ cells_xml = row_xml.elements.select { |e| e.name == 'table-cell' }
84
+ expect(cells_xml.size).to eq(2)
85
+
86
+ escape_elements = { ' ' => 's', "\t" => 'tab', "\n" => 'line-break' }
87
+
88
+ cell_xml = cells_xml[0]
89
+ p_xml = cell_xml.elements[1, 'p']
90
+
91
+ p_children = p_xml.to_a
92
+ expect(p_children.size).to eq(value_fragments.size)
93
+
94
+ value_fragments.each_with_index do |vf, i|
95
+ expected_element = escape_elements[vf]
96
+ if expected_element
97
+ element = p_children[i]
98
+ expect(element).to be_a(REXML::Element)
99
+ expect(element.name).to eq(expected_element)
100
+ expect(element.prefix).to eq('text')
101
+ else
102
+ text = p_children[i]
103
+ expect(text).to be_a(REXML::Text)
104
+ expect(text.value).to eq(vf)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ describe :to_xml do
111
+ it 'collapses consecutive nils' do
112
+ (2..7).each { |col| table.add_column("Column #{col}") }
113
+
114
+ row.set_value_at(0, 'Value 0')
115
+ row.set_value_at(2, 'Value 2')
116
+ row.set_value_at(6, 'Value 6')
117
+
118
+ row_xml = find_row_xml
119
+ cells_xml = row_xml.elements.select { |e| e.name == 'table-cell' }
120
+ expect(cells_xml.size).to eq(6)
121
+
122
+ expected_values = ['Value 0', nil, 'Value 2', nil, 'Value 6', nil]
123
+ expected_repeats = [nil, nil, nil, 3, nil, Table::MIN_COLUMNS - 7]
124
+ cells_xml.each_with_index do |cell_xml, i|
125
+ p = cell_xml.elements[1, 'p']
126
+ if (expected_value = expected_values[i])
127
+ expect(p).to be_a(REXML::Element)
128
+ texts = p.texts
129
+ expect(texts.size).to eq(1)
130
+ expect(texts[0].value).to eq(expected_value)
131
+ else
132
+ expect(p).to be_nil
133
+ end
134
+
135
+ num_cols_repeated = cell_xml['table:number-columns-repeated']
136
+ expected_repeat = expected_repeats[i]
137
+ if expected_repeat
138
+ expect(num_cols_repeated).to eq(expected_repeat.to_s)
139
+ else
140
+ expect(num_cols_repeated).to be_nil
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ describe :add_child do
147
+ it 'adds a cell' do
148
+ value = 'Value 0'
149
+ cell = TableCell.new(value, table: table)
150
+ row.add_child(cell)
151
+ expect(table.get_value_at(1, 0)).to eq(value)
152
+ end
153
+
154
+ it 'adds a non-cell child' do
155
+ child = ElementNode.new(:office, 'test', doc: table.doc)
156
+ row.add_child(child)
157
+ end
158
+
159
+ it "fails if there aren't enough columns" do
160
+ column_count = table.column_count
161
+ (0..column_count).each do |col_index|
162
+ value = "Value #{col_index}"
163
+ cell = TableCell.new(value, table: table)
164
+ if col_index < column_count
165
+ expect { row.add_child(cell) }.not_to raise_error
166
+ expect(table.get_value_at(1, col_index)).to eq(value)
167
+ else
168
+ expect { row.add_child(cell) }.to raise_error(ArgumentError)
169
+ expect(table.get_value_at(1, col_index)).to be_nil
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end