xlsxrb 0.1.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +64 -0
  3. data/.devcontainer/devcontainer.json +17 -0
  4. data/CHANGELOG.md +12 -0
  5. data/CODE_OF_CONDUCT.md +10 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +384 -0
  8. data/Rakefile +290 -0
  9. data/benchmark.rb +390 -0
  10. data/docs/ARCHITECTURE.md +488 -0
  11. data/docs/SPEC_SOURCES.md +42 -0
  12. data/docs/visual/VisualGallery.md +4684 -0
  13. data/docs/wasm/wasm_doc_helper.css +189 -0
  14. data/docs/wasm/wasm_doc_helper.js +320 -0
  15. data/lib/xlsxrb/elements/cell.rb +77 -0
  16. data/lib/xlsxrb/elements/column.rb +28 -0
  17. data/lib/xlsxrb/elements/row.rb +47 -0
  18. data/lib/xlsxrb/elements/types.rb +36 -0
  19. data/lib/xlsxrb/elements/workbook.rb +47 -0
  20. data/lib/xlsxrb/elements/worksheet.rb +50 -0
  21. data/lib/xlsxrb/elements.rb +15 -0
  22. data/lib/xlsxrb/ooxml/reader.rb +8048 -0
  23. data/lib/xlsxrb/ooxml/shared_strings_parser.rb +75 -0
  24. data/lib/xlsxrb/ooxml/styles_parser.rb +194 -0
  25. data/lib/xlsxrb/ooxml/utils.rb +122 -0
  26. data/lib/xlsxrb/ooxml/workbook_parser.rb +78 -0
  27. data/lib/xlsxrb/ooxml/workbook_writer.rb +1000 -0
  28. data/lib/xlsxrb/ooxml/worksheet_parser.rb +455 -0
  29. data/lib/xlsxrb/ooxml/worksheet_writer.rb +1008 -0
  30. data/lib/xlsxrb/ooxml/writer.rb +5450 -0
  31. data/lib/xlsxrb/ooxml/xml_builder.rb +113 -0
  32. data/lib/xlsxrb/ooxml/xml_parser.rb +68 -0
  33. data/lib/xlsxrb/ooxml/zip_generator.rb +162 -0
  34. data/lib/xlsxrb/ooxml/zip_reader.rb +158 -0
  35. data/lib/xlsxrb/ooxml/zip_writer.rb +213 -0
  36. data/lib/xlsxrb/ooxml.rb +22 -0
  37. data/lib/xlsxrb/style_builder.rb +241 -0
  38. data/lib/xlsxrb/version.rb +5 -0
  39. data/lib/xlsxrb.rb +1427 -0
  40. data/measure_memory.rb +42 -0
  41. data/sig/xlsxrb.rbs +23 -0
  42. data/vendor/sdk_runner/Program.cs +91 -0
  43. data/vendor/sdk_runner/sdk_runner.csproj +13 -0
  44. metadata +144 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xlsxrb
4
+ module Elements
5
+ # Represents an entire XLSX workbook.
6
+ Workbook = Data.define(:sheets, :shared_strings, :styles, :unmapped_data, :errors) do
7
+ def initialize(sheets: [], shared_strings: [], styles: {}, unmapped_data: {}, errors: nil)
8
+ computed_errors = errors || self.class.validate(sheets)
9
+ super(sheets: sheets.freeze, shared_strings: shared_strings.freeze, styles: styles,
10
+ unmapped_data: unmapped_data, errors: computed_errors.freeze)
11
+ end
12
+
13
+ def valid?
14
+ errors.empty?
15
+ end
16
+
17
+ # Returns the sheet at the given 0-based index or by name.
18
+ def sheet(identifier = 0)
19
+ case identifier
20
+ when Integer
21
+ sheets[identifier]
22
+ when String
23
+ sheets.find { |s| s.name == identifier }
24
+ end
25
+ end
26
+
27
+ # Returns sheet names.
28
+ def sheet_names
29
+ sheets.map(&:name)
30
+ end
31
+
32
+ def self.validate(sheets)
33
+ errs = []
34
+ errs << "sheets must be an Array (got #{sheets.class})" unless sheets.is_a?(Array)
35
+ if sheets.is_a?(Array)
36
+ errs << "workbook must have at least one sheet" if sheets.empty?
37
+ names = sheets.map(&:name)
38
+ if names.uniq.size != names.size
39
+ dups = names.select { |n| names.count(n) > 1 }.uniq
40
+ errs << "duplicate sheet name: #{dups.map(&:inspect).join(", ")} — sheet names must be unique"
41
+ end
42
+ end
43
+ errs
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xlsxrb
4
+ module Elements
5
+ # Represents a single worksheet in a workbook.
6
+ Worksheet = Data.define(:name, :rows, :columns, :charts, :unmapped_data, :errors) do
7
+ def initialize(name:, rows: [], columns: [], charts: [], unmapped_data: {}, errors: nil)
8
+ computed_errors = errors || self.class.validate(name, rows)
9
+ super(name: name, rows: rows.freeze, columns: columns.freeze, charts: charts.freeze,
10
+ unmapped_data: unmapped_data, errors: computed_errors.freeze)
11
+ end
12
+
13
+ def valid?
14
+ errors.empty?
15
+ end
16
+
17
+ # Returns the row at the given 0-based index, or nil.
18
+ def row_at(index)
19
+ rows.find { |r| r.index == index }
20
+ end
21
+
22
+ # Returns cell value at Excel-style reference (e.g. "A1").
23
+ def cell_value(ref)
24
+ parsed = Cell.parse_ref(ref)
25
+ return nil unless parsed
26
+
27
+ row_idx, col_idx = parsed
28
+ row = row_at(row_idx)
29
+ return nil unless row
30
+
31
+ cell = row.cell_at(col_idx)
32
+ cell&.value
33
+ end
34
+
35
+ def self.validate(name, rows)
36
+ errs = []
37
+ errs << "worksheet name must be a non-empty String (got #{name.inspect})" if name.nil? || (name.is_a?(String) && name.empty?)
38
+ errs << "rows must be an Array (got #{rows.class})" unless rows.is_a?(Array)
39
+ if rows.is_a?(Array)
40
+ indices = rows.map(&:index)
41
+ if indices.uniq.size != indices.size
42
+ dups = indices.select { |i| indices.count(i) > 1 }.uniq
43
+ errs << "duplicate row index: #{dups.join(", ")} — row indices within a sheet must be unique"
44
+ end
45
+ end
46
+ errs
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "elements/types"
4
+ require_relative "elements/cell"
5
+ require_relative "elements/row"
6
+ require_relative "elements/column"
7
+ require_relative "elements/worksheet"
8
+ require_relative "elements/workbook"
9
+
10
+ module Xlsxrb
11
+ # High-level domain model layer.
12
+ # Provides immutable Data classes representing Excel concepts.
13
+ module Elements
14
+ end
15
+ end