xlsxrb 0.1.11 → 0.1.13

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -34
  3. data/README.md +26 -21
  4. data/lib/xlsxrb/chart_builder.rb +18 -18
  5. data/lib/xlsxrb/dsl_helpers.rb +175 -0
  6. data/lib/xlsxrb/elements/cell.rb +56 -22
  7. data/lib/xlsxrb/elements/column.rb +27 -3
  8. data/lib/xlsxrb/elements/row.rb +27 -8
  9. data/lib/xlsxrb/elements/workbook.rb +27 -14
  10. data/lib/xlsxrb/elements/worksheet.rb +17 -1
  11. data/lib/xlsxrb/ooxml/cfb.rb +9 -6
  12. data/lib/xlsxrb/ooxml/crypto/agile.rb +5 -5
  13. data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +784 -0
  14. data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +3478 -0
  15. data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +1796 -0
  16. data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +428 -0
  17. data/lib/xlsxrb/ooxml/reader/listeners.rb +4 -6446
  18. data/lib/xlsxrb/ooxml/shared_strings_parser.rb +3 -5
  19. data/lib/xlsxrb/ooxml/utils.rb +5 -6
  20. data/lib/xlsxrb/ooxml/workbook_writer.rb +5 -7
  21. data/lib/xlsxrb/ooxml/worksheet_parser.rb +172 -9
  22. data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +1602 -0
  23. data/lib/xlsxrb/ooxml/writer/features_xml.rb +731 -0
  24. data/lib/xlsxrb/ooxml/writer/styles_xml.rb +348 -0
  25. data/lib/xlsxrb/ooxml/writer.rb +9 -2646
  26. data/lib/xlsxrb/ooxml/xml_builder.rb +31 -2
  27. data/lib/xlsxrb/ooxml/zip_generator.rb +26 -5
  28. data/lib/xlsxrb/ooxml/zip_reader.rb +364 -78
  29. data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
  30. data/lib/xlsxrb/stream_sheet.rb +94 -6
  31. data/lib/xlsxrb/stream_writer.rb +48 -115
  32. data/lib/xlsxrb/style_builder.rb +10 -1
  33. data/lib/xlsxrb/version.rb +1 -1
  34. data/lib/xlsxrb/workbook_builder.rb +1 -1
  35. data/lib/xlsxrb/worksheet_builder.rb +19 -81
  36. data/lib/xlsxrb.rb +449 -171
  37. data/sig/generated/xlsxrb/chart_builder.rbs +36 -36
  38. data/sig/generated/xlsxrb/dsl_helpers.rbs +92 -0
  39. data/sig/generated/xlsxrb/elements/cell.rbs +29 -0
  40. data/sig/generated/xlsxrb/elements/column.rbs +32 -21
  41. data/sig/generated/xlsxrb/elements/row.rbs +94 -14
  42. data/sig/generated/xlsxrb/elements/workbook.rbs +102 -10
  43. data/sig/generated/xlsxrb/elements/worksheet.rbs +9 -0
  44. data/sig/generated/xlsxrb/ooxml/reader/listeners/core_listeners.rbs +173 -0
  45. data/sig/generated/xlsxrb/ooxml/reader/listeners/drawing_listeners.rbs +400 -0
  46. data/sig/generated/xlsxrb/ooxml/reader/listeners/feature_listeners.rbs +442 -0
  47. data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +80 -0
  48. data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +0 -1072
  49. data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +0 -2
  50. data/sig/generated/xlsxrb/ooxml/utils.rbs +2 -0
  51. data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +5 -4
  52. data/sig/generated/xlsxrb/ooxml/writer/drawing_xml.rbs +79 -0
  53. data/sig/generated/xlsxrb/ooxml/writer/features_xml.rbs +83 -0
  54. data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +53 -0
  55. data/sig/generated/xlsxrb/ooxml/writer.rbs +6 -185
  56. data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +18 -0
  57. data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +15 -0
  58. data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +96 -8
  59. data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
  60. data/sig/generated/xlsxrb/stream_sheet.rbs +43 -3
  61. data/sig/generated/xlsxrb/style_builder.rbs +7 -0
  62. data/sig/generated/xlsxrb.rbs +18 -11
  63. metadata +22 -144
  64. data/.devcontainer/Dockerfile +0 -65
  65. data/.devcontainer/devcontainer.json +0 -17
  66. data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
  67. data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
  68. data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
  69. data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
  70. data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
  71. data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
  72. data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
  73. data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
  74. data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
  75. data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
  76. data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
  77. data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
  78. data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
  79. data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
  80. data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
  81. data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
  82. data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
  83. data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
  84. data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
  85. data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
  86. data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
  87. data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
  88. data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
  89. data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
  90. data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
  91. data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
  92. data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
  93. data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
  94. data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
  95. data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
  96. data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
  97. data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
  98. data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
  99. data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
  100. data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
  101. data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
  102. data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
  103. data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
  104. data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
  105. data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
  106. data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
  107. data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
  108. data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
  109. data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
  110. data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
  111. data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
  112. data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
  113. data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
  114. data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
  115. data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
  116. data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
  117. data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
  118. data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
  119. data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
  120. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
  121. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
  122. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
  123. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
  124. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
  125. data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
  126. data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
  127. data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
  128. data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
  129. data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
  130. data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
  131. data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
  132. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
  133. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
  134. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
  135. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
  136. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
  137. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
  138. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
  139. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
  140. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
  141. data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
  142. data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
  143. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
  144. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
  145. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
  146. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
  147. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
  148. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
  149. data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
  150. data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
  151. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
  152. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
  153. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
  154. data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
  155. data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
  156. data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
  157. data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
  158. data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
  159. data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
  160. data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
  161. data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
  162. data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
  163. data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
  164. data/Rakefile +0 -567
  165. data/Steepfile +0 -18
  166. data/benchmark.rb +0 -406
  167. data/docs/ARCHITECTURE.md +0 -514
  168. data/docs/DEVELOPMENT.md +0 -133
  169. data/docs/PEER_LIBRARIES.md +0 -121
  170. data/docs/QUALITY_ASSURANCE.md +0 -25
  171. data/docs/SPEC_SOURCES.md +0 -48
  172. data/docs/assets/benchmark_results.svg +0 -144
  173. data/docs/assets/lsp_autocompletion.png +0 -0
  174. data/docs/assets/playground_preview.png +0 -0
  175. data/docs/coi-serviceworker.js +0 -82
  176. data/docs/office_thread.js +0 -77
  177. data/docs/preview.html +0 -719
  178. data/docs/visual/VisualGallery.md +0 -4672
  179. data/docs/wasm/ruby.wasm +0 -0
  180. data/docs/wasm/wasm_doc_helper.css +0 -351
  181. data/docs/wasm/wasm_doc_helper.js +0 -439
  182. data/docs/zeta.js +0 -1107
  183. data/rbs_collection.lock.yaml +0 -252
  184. data/rbs_collection.yaml +0 -19
  185. data/vendor/sdk_runner/Program.cs +0 -93
  186. data/vendor/sdk_runner/sdk_runner.csproj +0 -13
data/benchmark.rb DELETED
@@ -1,406 +0,0 @@
1
- # frozen_string_literal: true
2
- # rubocop:disable all
3
-
4
- require "json"
5
- require "open3"
6
- require "fileutils"
7
- require "bundler/inline"
8
-
9
- puts "Ensuring benchmark peer ecosystem gems are available (bundler/inline)..."
10
- gemfile(true) do
11
- source "https://rubygems.org"
12
- gem "rubyzip", ">= 2.3.0"
13
- gem "caxlsx", "4.5.0"
14
- gem "xlsxtream", "3.1.0"
15
- gem "fast_excel", "0.5.0", platform: :mri
16
- gem "rubyXL", "3.4.38"
17
- gem "roo", "3.0.0"
18
- gem "creek", "2.6.3"
19
- gem "xsv", "1.4.1"
20
- gem "write_xlsx", "1.15.0"
21
- gem "simple_xlsx_reader", "5.1.0"
22
- end
23
-
24
- RUNS = (ENV["RUNS"] || "3").to_i
25
- ROWS = (ARGV[0] || "100000").to_i
26
- COLS = (ARGV[1] || "10").to_i
27
-
28
- AVAILABLE_GEMS = {
29
- "xlsxrb_stream" => true,
30
- "xlsxrb_inmemory" => true,
31
- "xlsxtream" => true,
32
- "fast_excel" => true,
33
- "caxlsx" => true,
34
- "write_xlsx" => true,
35
- "rubyXL" => true,
36
- "creek" => true,
37
- "roo" => true,
38
- "xsv" => true,
39
- "simple_xlsx_reader" => true
40
- }
41
-
42
- puts "=" * 80
43
- puts "Benchmarking Excel Libraries (#{ROWS} rows x #{COLS} cols = #{ROWS * COLS} cells)"
44
- puts "Runs per benchmark: #{RUNS} (Median reported, Mean calculated)"
45
- puts "Ruby: #{RUBY_DESCRIPTION}"
46
- puts "=" * 80
47
-
48
- RUNNER_SCRIPT = <<~'RUBY'
49
- require "json"
50
- require "stringio"
51
-
52
- def measure
53
- gc_before = GC.stat[:count]
54
- t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
55
-
56
- yield
57
-
58
- t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
59
- gc_after = GC.stat[:count]
60
-
61
- # Peak memory in MB via /proc/self/status or getrusage
62
- peak_mb = 0.0
63
- if File.exist?("/proc/self/status")
64
- status = File.read("/proc/self/status")
65
- if status =~ /VmHWM:\s+(\d+)\s+kB/i
66
- peak_mb = $1.to_f / 1024.0
67
- elsif status =~ /VmRSS:\s+(\d+)\s+kB/i
68
- peak_mb = $1.to_f / 1024.0
69
- end
70
- end
71
-
72
- {
73
- time: (t1 - t0),
74
- peak_memory_mb: peak_mb,
75
- gc_count: (gc_after - gc_before)
76
- }
77
- end
78
-
79
- def generate_row(r, cols)
80
- base = [r + 1, "User #{r + 1}", 123.45, true, "Active", (r + 1) * 10, "Tokyo", 99.9, false, "Item #{r % 50}"]
81
- if cols <= base.size
82
- base.first(cols)
83
- else
84
- base + Array.new(cols - base.size) { |c| "col_#{c}_#{r}" }
85
- end
86
- end
87
-
88
- lib = ARGV[0]
89
- mode = ARGV[1] # "write" or "read"
90
- rows = ARGV[2].to_i
91
- cols = ARGV[3].to_i
92
- filename = ARGV[4]
93
-
94
- result = case [lib, mode]
95
- when ["xlsxtream", "write"]
96
- require "xlsxtream"
97
- measure do
98
- Xlsxtream::Workbook.open(filename) do |wb|
99
- wb.write_worksheet("Data") do |sheet|
100
- rows.times do |r|
101
- sheet << generate_row(r, cols)
102
- end
103
- end
104
- end
105
- end
106
- when ["fast_excel", "write"]
107
- require "fast_excel"
108
- measure do
109
- wb = FastExcel.open(filename)
110
- sheet = wb.add_worksheet("Data")
111
- rows.times do |r|
112
- sheet.append_row(generate_row(r, cols))
113
- end
114
- wb.close
115
- end
116
- when ["caxlsx", "write"]
117
- require "caxlsx"
118
- measure do
119
- p = Axlsx::Package.new
120
- wb = p.workbook
121
- wb.add_worksheet(name: "Data") do |sheet|
122
- rows.times do |r|
123
- sheet.add_row(generate_row(r, cols))
124
- end
125
- end
126
- p.serialize(filename)
127
- end
128
- when ["write_xlsx", "write"]
129
- require "write_xlsx"
130
- measure do
131
- wb = WriteXLSX.new(filename)
132
- sheet = wb.add_worksheet("Data")
133
- rows.times do |r|
134
- sheet.write_row(r, 0, generate_row(r, cols))
135
- end
136
- wb.close
137
- end
138
- when ["rubyXL", "write"]
139
- require "rubyXL"
140
- measure do
141
- wb = RubyXL::Workbook.new
142
- sheet = wb[0]
143
- sheet.sheet_name = "Data"
144
- rows.times do |r|
145
- row_data = generate_row(r, cols)
146
- row_data.each_with_index do |val, c|
147
- sheet.add_cell(r, c, val)
148
- end
149
- end
150
- wb.write(filename)
151
- end
152
- when ["xlsxrb_stream", "write"]
153
- require_relative "lib/xlsxrb"
154
- measure do
155
- Xlsxrb.write(filename) do |wb|
156
- wb.sheet("Data") do |sheet|
157
- rows.times do |r|
158
- sheet.row(generate_row(r, cols))
159
- end
160
- end
161
- end
162
- end
163
- when ["xlsxrb_inmemory", "write"]
164
- require_relative "lib/xlsxrb"
165
- measure do
166
- wb = Xlsxrb.build do |b|
167
- b.sheet("Data") do |s|
168
- rows.times do |r|
169
- s.row(generate_row(r, cols))
170
- end
171
- end
172
- end
173
- Xlsxrb.write(filename, wb)
174
- end
175
- when ["xlsxrb_stream", "read"]
176
- require_relative "lib/xlsxrb"
177
- measure do
178
- count = 0
179
- Xlsxrb.read(filename) do |sheet|
180
- sheet.each do |row|
181
- row.cells.each do |cell|
182
- _val = cell.value
183
- count += 1
184
- end
185
- end
186
- end
187
- end
188
- when ["xlsxrb_inmemory", "read"]
189
- require_relative "lib/xlsxrb"
190
- measure do
191
- wb = Xlsxrb.read(filename).load
192
- count = 0
193
- wb.sheets.each do |sheet|
194
- sheet.rows.each do |row|
195
- row.cells.each do |cell|
196
- _val = cell.value
197
- count += 1
198
- end
199
- end
200
- end
201
- end
202
- when ["creek", "read"]
203
- require "creek"
204
- measure do
205
- creek = Creek::Book.new(filename)
206
- count = 0
207
- creek.sheets.each do |sheet|
208
- sheet.rows.each do |row|
209
- row.each_value do |_val|
210
- count += 1
211
- end
212
- end
213
- end
214
- end
215
- when ["roo", "read"]
216
- require "roo"
217
- measure do
218
- xlsx = Roo::Excelx.new(filename)
219
- count = 0
220
- xlsx.each_row_streaming do |row|
221
- row.each do |cell|
222
- _val = cell&.value
223
- count += 1
224
- end
225
- end
226
- end
227
- when ["xsv", "read"]
228
- require "xsv"
229
- measure do
230
- x = Xsv.open(filename)
231
- count = 0
232
- x.sheets.each do |sheet|
233
- sheet.each do |row|
234
- row.each do |_val|
235
- count += 1
236
- end
237
- end
238
- end
239
- end
240
- when ["simple_xlsx_reader", "read"]
241
- gem "rubyzip", "~> 1.3.0"
242
- require "simple_xlsx_reader"
243
- measure do
244
- doc = SimpleXlsxReader.open(filename)
245
- count = 0
246
- doc.sheets.each do |sheet|
247
- sheet.rows.each do |row|
248
- row.each do |_val|
249
- count += 1
250
- end
251
- end
252
- end
253
- end
254
- when ["rubyXL", "read"]
255
- require "rubyXL"
256
- measure do
257
- wb = RubyXL::Parser.parse(filename)
258
- count = 0
259
- wb.worksheets.each do |sheet|
260
- sheet.each do |row|
261
- next unless row
262
- row.cells.each do |cell|
263
- _val = cell&.value
264
- count += 1
265
- end
266
- end
267
- end
268
- end
269
- else
270
- raise "Unknown benchmark target: #{lib} #{mode}"
271
- end
272
-
273
- puts result.to_json
274
- RUBY
275
-
276
- runner_file = "benchmark_runner.rb"
277
- File.write(runner_file, RUNNER_SCRIPT)
278
-
279
- def run_isolated(lib, mode, rows, cols, filename)
280
- cmd = ["ruby", "-Ilib", "benchmark_runner.rb", lib, mode, rows.to_s, cols.to_s, filename]
281
- stdout, stderr, status = Bundler.with_unbundled_env do
282
- Open3.capture3(*cmd)
283
- end
284
- unless status.success?
285
- warn "Failed to run #{lib} #{mode}: #{stderr}"
286
- return nil
287
- end
288
- JSON.parse(stdout.strip, symbolize_names: true)
289
- end
290
-
291
- def run_benchmark_series(name, lib, mode, rows, cols, filename, runs)
292
- unless AVAILABLE_GEMS[lib]
293
- puts "Skipping #{name} (gem not installed)"
294
- return nil
295
- end
296
-
297
- print "Running #{name} (#{runs} runs)... "
298
- $stdout.flush
299
- results = []
300
- runs.times do |_i|
301
- File.delete(filename) if File.exist?(filename) && mode == "write"
302
- res = run_isolated(lib, mode, rows, cols, filename)
303
- if res
304
- results << res
305
- print "#{res[:time].round(2)}s "
306
- $stdout.flush
307
- else
308
- print "ERR "
309
- $stdout.flush
310
- end
311
- end
312
- puts
313
- return nil if results.empty?
314
-
315
- times = results.map { |r| r[:time] }.sort
316
- mems = results.map { |r| r[:peak_memory_mb] }.sort
317
- gcs = results.map { |r| r[:gc_count] }.sort
318
-
319
- median_time = times[times.size / 2]
320
- mean_time = times.sum / times.size
321
- median_mem = mems[mems.size / 2]
322
- median_gc = gcs[gcs.size / 2]
323
-
324
- {
325
- name: name,
326
- median_time: median_time,
327
- mean_time: mean_time,
328
- median_mem: median_mem,
329
- median_gc: median_gc
330
- }
331
- end
332
-
333
- # 1. Generate a standard reference file for reading benchmarks
334
- ref_file = "bench_reference_data.xlsx"
335
- puts "\n[Setup] Generating reference file (#{ROWS} x #{COLS}) for read benchmarks..."
336
- run_isolated("xlsxrb_stream", "write", ROWS, COLS, ref_file)
337
-
338
- # 2. Benchmark Write
339
- puts "\n=== Benchmarking Write Performance ==="
340
- write_targets = [
341
- ["xlsxtream 3.1.0", "xlsxtream", "Streaming", "Inline String"],
342
- ["xlsxrb (Streaming)", "xlsxrb_stream", "Streaming", "SST (Shared)"],
343
- ["fast_excel 0.5.0 (C)", "fast_excel", "Streaming", "SST (Shared)"],
344
- ["caxlsx 4.5.0", "caxlsx", "In-Memory", "Inline String"],
345
- ["write_xlsx 1.15.0", "write_xlsx", "In-Memory", "SST (Shared)"],
346
- ["xlsxrb (In-Memory)", "xlsxrb_inmemory", "In-Memory", "SST (Shared)"],
347
- ["rubyXL 3.4.38", "rubyXL", "In-Memory", "Inline String"]
348
- ]
349
-
350
- write_results = []
351
- write_targets.each do |name, lib, model, storage|
352
- target_file = "bench_write_#{lib}.xlsx"
353
- res = run_benchmark_series(name, lib, "write", ROWS, COLS, target_file, RUNS)
354
- if res
355
- res[:model] = model
356
- res[:storage] = storage
357
- write_results << res
358
- end
359
- FileUtils.rm_f(target_file)
360
- end
361
-
362
- # 3. Benchmark Read
363
- puts "\n=== Benchmarking Read Performance ==="
364
- read_targets = [
365
- ["xlsxrb (Streaming)", "xlsxrb_stream", "Streaming"],
366
- ["xlsxrb (In-Memory)", "xlsxrb_inmemory", "In-Memory"],
367
- ["creek 2.6.3", "creek", "Streaming"],
368
- ["roo 3.0.0", "roo", "Streaming"],
369
- ["simple_xlsx_reader 5.1.0", "simple_xlsx_reader", "Streaming"],
370
- ["xsv 1.4.1", "xsv", "Streaming"],
371
- ["rubyXL 3.4.38", "rubyXL", "In-Memory"]
372
- ]
373
-
374
- read_results = []
375
- read_targets.each do |name, lib, model|
376
- res = run_benchmark_series(name, lib, "read", ROWS, COLS, ref_file, RUNS)
377
- if res
378
- res[:model] = model
379
- read_results << res
380
- end
381
- end
382
-
383
- # Cleanup
384
- FileUtils.rm_f(ref_file)
385
- FileUtils.rm_f(runner_file)
386
-
387
- # Print Tables
388
- puts "\n" + ("=" * 80)
389
- puts "### Write Performance (#{ROWS * COLS} cells: #{ROWS} rows x #{COLS} cols)"
390
- puts ""
391
- puts "| Library | Model | Write String Storage | Time (Median) | Time (Mean) | Peak Memory | GC Count |"
392
- puts "| ---------------------- | ----------- | -------------------- | ------------- | ----------- | ----------- | -------- |"
393
- write_results.sort_by { |r| r[:median_time] }.each do |r|
394
- printf "| %-22s | %-11s | %-20s | %6.2f s | %6.2f s | %7.1f MB | %6.1f |\n",
395
- r[:name], r[:model], r[:storage], r[:median_time], r[:mean_time], r[:median_mem], r[:median_gc]
396
- end
397
-
398
- puts "\n### Read Performance (#{ROWS * COLS} cells: #{ROWS} rows x #{COLS} cols)"
399
- puts ""
400
- puts "| Library | Model | Time (Median) | Time (Mean) | Peak Memory | GC Count |"
401
- puts "| ---------------------- | ----------- | ------------- | ----------- | ----------- | -------- |"
402
- read_results.sort_by { |r| r[:median_time] }.each do |r|
403
- printf "| %-22s | %-11s | %6.2f s | %6.2f s | %7.1f MB | %6.1f |\n",
404
- r[:name], r[:model], r[:median_time], r[:mean_time], r[:median_mem], r[:median_gc]
405
- end
406
- puts "=" * 80