xlsxrb 0.1.8 → 0.1.10
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -2
- data/README.md +77 -222
- data/Rakefile +22 -3
- data/docs/DEVELOPMENT.md +11 -3
- data/docs/PEER_LIBRARIES.md +121 -0
- data/docs/QUALITY_ASSURANCE.md +3 -4
- data/docs/SPEC_SOURCES.md +7 -1
- data/docs/assets/benchmark_results.svg +144 -0
- data/docs/assets/lsp_autocompletion.png +0 -0
- data/docs/assets/playground_preview.png +0 -0
- data/docs/visual/VisualGallery.md +183 -0
- data/docs/wasm/ruby.wasm +0 -0
- data/lib/xlsxrb/chart_builder.rb +333 -0
- data/lib/xlsxrb/elements/row.rb +2 -2
- data/lib/xlsxrb/elements/workbook.rb +3 -3
- data/lib/xlsxrb/elements/worksheet.rb +4 -4
- data/lib/xlsxrb/ooxml/cfb.rb +490 -0
- data/lib/xlsxrb/ooxml/crypto/agile.rb +285 -0
- data/lib/xlsxrb/ooxml/crypto/standard.rb +152 -0
- data/lib/xlsxrb/ooxml/crypto.rb +60 -0
- data/lib/xlsxrb/ooxml/reader/listeners.rb +6450 -0
- data/lib/xlsxrb/ooxml/reader.rb +2 -6438
- data/lib/xlsxrb/ooxml/workbook_writer.rb +10 -10
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +161 -116
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +5 -1
- data/lib/xlsxrb/stream_row.rb +7 -5
- data/lib/xlsxrb/stream_sheet.rb +125 -0
- data/lib/xlsxrb/stream_writer.rb +1646 -0
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +309 -0
- data/lib/xlsxrb/worksheet_builder.rb +691 -0
- data/lib/xlsxrb.rb +350 -2989
- data/sig/generated/xlsxrb/chart_builder.rbs +269 -0
- data/sig/generated/xlsxrb/elements/worksheet.rbs +8 -5
- data/sig/generated/xlsxrb/ooxml/cfb.rbs +121 -0
- data/sig/generated/xlsxrb/ooxml/crypto/agile.rbs +46 -0
- data/sig/generated/xlsxrb/ooxml/crypto/standard.rbs +28 -0
- data/sig/generated/xlsxrb/ooxml/crypto.rbs +17 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +1074 -0
- data/sig/generated/xlsxrb/ooxml/reader.rbs +0 -1067
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +3 -3
- data/sig/generated/xlsxrb/stream_row.rbs +7 -4
- data/sig/generated/xlsxrb/stream_sheet.rbs +100 -0
- data/sig/generated/xlsxrb/stream_writer.rbs +954 -0
- data/sig/generated/xlsxrb/workbook_builder.rbs +146 -0
- data/sig/generated/xlsxrb/worksheet_builder.rbs +349 -0
- data/sig/generated/xlsxrb.rbs +131 -1569
- data/vendor/sdk_runner/Program.cs +3 -1
- metadata +25 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f865582e17bc03ede4f300f348f6166a666d82cd088f42a501a6518979a7b999
|
|
4
|
+
data.tar.gz: 25ba8105a6ca0318aa9bb4766a0de59da4818d18d226d212f1ec941fde3082c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6e35bcdabcff60af9084c9860fcf1838abfb507065a2eabb1004f40f16ab6cf6f7a11d136bbd83e97f46f0e460b906601db1584ef23b00c05076bfc240417baf
|
|
7
|
+
data.tar.gz: 8d4597dad8fe327f4ad887934a7f2bc18fcd65b606ef265ab38bcef0a0ae2565dc09f9e7f04dd8dc5bf7c3403fadc2a265d3ce074c9fd623489d8f4004065802
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
|
-
## [
|
|
2
|
-
|
|
1
|
+
## [0.1.10] - 2026-08-19
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- **Peer Libraries Ecosystem Guide ([docs/PEER_LIBRARIES.md](docs/PEER_LIBRARIES.md))**: Introduced a respectful overview of the Ruby XLSX ecosystem featuring official self-descriptions, architectural tradeoffs (SST vs. Inline Strings, Streaming vs. In-Memory), and reproducible benchmarks across 9 popular Ruby XLSX gems.
|
|
5
|
+
- **Visual Assets & Screen Previews**:
|
|
6
|
+
- Embedded interactive WebAssembly Playground live demo preview in `README.md`.
|
|
7
|
+
- Added real-world Ruby LSP autocompletion and RBS type hint preview in `README.md`.
|
|
8
|
+
- Created accurate, neutral linear-scale SVG benchmark performance chart.
|
|
9
|
+
- **Enterprise-Grade Test Suite Expansion**:
|
|
10
|
+
- **ECMA-376 XSD Schema Validation**: Comprehensive XML schema validation suite ensuring strict element ordering and ISO/IEC 29500 compliance.
|
|
11
|
+
- **Contract Testing Suite**: Comprehensive parity verification between Streaming (`Xlsxrb.write`) and In-Memory (`Xlsxrb.build`) APIs.
|
|
12
|
+
- **Property-Based Testing (PBT)**: Expanded automated random generation tests for Row/Column invariants, styles, and edge cases.
|
|
13
|
+
- **Visual Regression Testing (VRT)**: Added new visual baselines for table styles, drawing shapes, and pivot tables.
|
|
14
|
+
- **E2E Interoperability Suite**: Added tests for namespace-prefixed XML streaming, conditional formatting, and table structures.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **WebAssembly (ruby.wasm) Compatibility**: Bundled `pp` and `prettyprint` standard libraries in `ruby.wasm` package to resolve REXML LoadError during browser-based evaluation.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **Streamlined README**: Refactored README from 363 to 175 lines, focusing on core motivation, clean 4-column feature matrix, concise usage examples, and direct links to specialized documentation.
|
|
21
|
+
|
|
22
|
+
## [0.1.9] - 2026-08-18
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- **Password Protection & Document Encryption ([MS-OFFCRYPTO] / [MS-CFB])**: Full native Pure-Ruby support for reading, writing, and modifying password-protected Excel spreadsheets without any external C-extension dependencies.
|
|
26
|
+
- **Standard Encryption**: AES-128-ECB and SHA-1 Key Derivation with CryptoAPI 50,000-spin hashing, fully interoperable across Microsoft Excel, LibreOffice, and Google Sheets.
|
|
27
|
+
- **Agile Encryption**: Modern AES-256-CBC, PBKDF2/SHA-512, and HMAC-SHA512 data integrity verification.
|
|
28
|
+
- **Compound File Binary (CFB) Engine**: Pure-Ruby reader and writer for OLE structured storage containers with Mini Stream, FAT/MiniFAT sectors, and Red-Black tree directory management.
|
|
29
|
+
- **Transparent Public API Integration**: Added `password:` and `encryption_mode:` arguments to `Xlsxrb.read`, `Xlsxrb.write`, and `Xlsxrb.modify`.
|
|
30
|
+
- **Security & Threat Model Hardening**:
|
|
31
|
+
- Constant-time hash verification via `OpenSSL.secure_compare` to prevent timing attacks (CWE-208).
|
|
32
|
+
- CSPRNG-backed salt, IV, and session key generation via `SecureRandom` (CWE-330).
|
|
33
|
+
- Robust DoS defense: spinCount limit ($\le 10\text{M}$), CFB circular sector chain loop detection in directory/FAT parsing, and `total_size` bounds validation (CWE-400, CWE-835).
|
|
34
|
+
- Strict exception hierarchy (`EncryptedFileError`, `InvalidPasswordError`, `DecryptionError`).
|
|
35
|
+
- **Cross-Platform & Interoperability Validation**: Bidirectional validation with Microsoft .NET OpenXML SDK and LibreOffice Calc.
|
|
36
|
+
- **WebAssembly (ruby.wasm) Support**: Pre-packaged `docs/wasm/ruby.wasm` updated with document encryption support for browser playground.
|
|
3
37
|
|
|
4
38
|
## [0.1.8] - 2026-08-18
|
|
5
39
|
|
data/README.md
CHANGED
|
@@ -4,83 +4,61 @@ A Ruby library for reading and writing XLSX files with streaming support.
|
|
|
4
4
|
|
|
5
5
|
## Motivation
|
|
6
6
|
|
|
7
|
-
The Ruby ecosystem already has great XLSX libraries
|
|
8
|
-
|
|
9
|
-
| Library
|
|
10
|
-
|
|
|
11
|
-
| [roo](https://rubygems.org/gems/roo)
|
|
12
|
-
| [creek](https://rubygems.org/gems/creek)
|
|
13
|
-
| [xsv](https://rubygems.org/gems/xsv)
|
|
14
|
-
| [simple_xlsx_reader](https://rubygems.org/gems/simple_xlsx_reader) | ✅ | ❌ |
|
|
15
|
-
| [caxlsx / axlsx](https://rubygems.org/gems/caxlsx)
|
|
16
|
-
| [write_xlsx](https://rubygems.org/gems/write_xlsx)
|
|
17
|
-
| [xlsxtream](https://rubygems.org/gems/xlsxtream)
|
|
18
|
-
| [fast_excel](https://rubygems.org/gems/fast_excel)
|
|
19
|
-
| [rubyXL](https://rubygems.org/gems/rubyXL)
|
|
20
|
-
| **[xlsxrb](https://github.com/niku/xlsxrb)**
|
|
21
|
-
|
|
22
|
-
Each of these libraries makes deliberate
|
|
23
|
-
* **
|
|
24
|
-
* **
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`xlsxrb` is born from a different premise. We believe that Advanced Agentic AI (AI Coders) can help manage this maintenance demand. By utilizing AI agents to automate rigorous E2E testing, visual regression testing, specification compliance checks, and documentation updates, we can reconcile these competing engineering requirements. This allows us to build and continuously maintain a feature-rich, high-performance, and deeply compatible "all-in-one" XLSX library that remains sustainable for the long run.
|
|
7
|
+
The Ruby ecosystem already has great XLSX libraries, each designed for specific tradeoffs:
|
|
8
|
+
|
|
9
|
+
| Library | Read | Write | Streaming | In-Memory |
|
|
10
|
+
| :--- | :---: | :---: | :---: | :---: |
|
|
11
|
+
| [roo](https://rubygems.org/gems/roo) | ✅ | ❌ | ✅ | ❌ |
|
|
12
|
+
| [creek](https://rubygems.org/gems/creek) | ✅ | ❌ | ✅ | ❌ |
|
|
13
|
+
| [xsv](https://rubygems.org/gems/xsv) | ✅ | ❌ | ✅ | ❌ |
|
|
14
|
+
| [simple_xlsx_reader](https://rubygems.org/gems/simple_xlsx_reader) | ✅ | ❌ | ✅ | ❌ |
|
|
15
|
+
| [caxlsx / axlsx](https://rubygems.org/gems/caxlsx) | ❌ | ✅ | ❌ | ✅ |
|
|
16
|
+
| [write_xlsx](https://rubygems.org/gems/write_xlsx) | ❌ | ✅ | ❌ | ✅ |
|
|
17
|
+
| [xlsxtream](https://rubygems.org/gems/xlsxtream) | ❌ | ✅ | ✅ | ❌ |
|
|
18
|
+
| [fast_excel](https://rubygems.org/gems/fast_excel) | ❌ | ✅ | ✅ | ❌ |
|
|
19
|
+
| [rubyXL](https://rubygems.org/gems/rubyXL) | ✅ | ✅ | ❌ | ✅ |
|
|
20
|
+
| **[xlsxrb](https://github.com/niku/xlsxrb)** | **✅** | **✅** | **✅** | **✅** |
|
|
21
|
+
|
|
22
|
+
Each of these libraries makes deliberate architectural choices:
|
|
23
|
+
* **Streaming Model**: Writes or reads rows sequentially on-the-fly to maintain a constant $O(1)$, low-memory footprint regardless of dataset size.
|
|
24
|
+
* **In-Memory Model**: Builds a complete document object model, offering flexible random access, cell updates, and document templates at the cost of memory usage on large spreadsheets.
|
|
25
|
+
|
|
26
|
+
Traditionally, maintaining an all-in-one gem that offers both reading and writing across both streaming and in-memory models, alongside rich OOXML features, high performance, and strict compatibility, presents an inherent open-source challenge: the cumulative maintenance overhead often exceeds the capacity of individual human maintainers.
|
|
27
|
+
|
|
28
|
+
`xlsxrb` is built on a modern premise: **Advanced Agentic AI (AI Coders) can sustainably handle this maintenance demand.** By utilizing AI agents to automate end-to-end testing, visual regression testing, specification compliance verification, and documentation updates, `xlsxrb` delivers a fast, specification-compliant, and fully-featured XLSX library built for long-term sustainability.
|
|
31
29
|
|
|
32
30
|
### Design Principles
|
|
33
31
|
|
|
34
|
-
- Minimal Dependencies
|
|
35
|
-
- Streaming Support
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
- Modern Ruby 4.0+: Built for the future with Ruby 4.0 or higher.
|
|
32
|
+
- **Minimal Dependencies**: Zero core logic dependencies. Built purely on the Ruby standard library and bundled gems (`zlib`, `rexml`, etc.). The only runtime dependency is `opentelemetry-api` (zero-overhead no-op when unconfigured).
|
|
33
|
+
- **Streaming Support**: True $O(1)$ constant memory streaming for both reading and writing massive spreadsheets.
|
|
34
|
+
- **Strict OpenXML Interoperability**: Fully compliant with ISO/IEC 29500 (ECMA-376) and validated continuously against the official Microsoft [Open XML SDK](https://github.com/dotnet/Open-XML-SDK).
|
|
35
|
+
- **AI-Assisted Sustainability**: Leveraging AI coding agents for automated quality assurance, E2E validation, and continuous feature expansion.
|
|
36
|
+
- **Modern Ruby**: Built for Ruby 4.0 or higher.
|
|
40
37
|
|
|
41
38
|
## Installation
|
|
42
39
|
|
|
43
40
|
```bash
|
|
44
41
|
bundle add xlsxrb
|
|
42
|
+
# Or without Bundler: gem install xlsxrb
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
Or without Bundler:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
gem install xlsxrb
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
On Ruby 4+, some components used by `xlsxrb` and its test suite are shipped as bundled gems rather than built-in default libraries. When using Bundler, those bundled gems are resolved and installed in the usual way.
|
|
54
|
-
|
|
55
45
|
## Interactive Playground (WebAssembly)
|
|
56
46
|
|
|
57
|
-
|
|
47
|
+
Try `xlsxrb` directly in your browser without installing anything!
|
|
58
48
|
|
|
59
49
|
[👉 Try the Live Demo / Interactive Playground](https://niku.github.io/xlsxrb/docs/visual/VisualGallery_md.html)
|
|
60
50
|
|
|
61
|
-
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="docs/assets/playground_preview.png" width="100%" alt="Interactive WebAssembly Playground with Live LibreOffice Preview"/>
|
|
53
|
+
</p>
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
1. Generate the WebAssembly bundle and interactive RDoc:
|
|
65
|
-
```bash
|
|
66
|
-
bundle exec rake doc
|
|
67
|
-
```
|
|
68
|
-
2. Start the local preview server:
|
|
69
|
-
```bash
|
|
70
|
-
bundle exec rake doc:preview
|
|
71
|
-
```
|
|
72
|
-
3. Open [http://localhost:8000](http://localhost:8000) in your browser, hover over any code block, and click the "Live Preview" or "Download XLSX" buttons!
|
|
55
|
+
You can also browse 50+ rendered visual examples across all features in the [Visual Examples Gallery](docs/visual/VisualGallery.md).
|
|
73
56
|
|
|
74
57
|
## Usage
|
|
75
58
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
For visual demonstrations of various features, check the [Visual Examples Gallery](docs/visual/VisualGallery.md).
|
|
59
|
+
### Quick Start: Streaming (Recommended for Large Files)
|
|
79
60
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
#### Streaming Write
|
|
83
|
-
Generate large files efficiently with $O(1)$ constant memory by writing data directly to the stream:
|
|
61
|
+
#### Streaming Write ($O(1)$ Memory)
|
|
84
62
|
```ruby
|
|
85
63
|
require "xlsxrb"
|
|
86
64
|
|
|
@@ -93,12 +71,10 @@ Xlsxrb.write("large_output.xlsx") do |writer|
|
|
|
93
71
|
end
|
|
94
72
|
```
|
|
95
73
|
|
|
96
|
-
#### Streaming Read
|
|
97
|
-
Read rows and cells lazily one at a time with $O(1)$ constant memory (even for wide sheets with thousands of columns):
|
|
74
|
+
#### Streaming Read ($O(1)$ Memory)
|
|
98
75
|
```ruby
|
|
99
76
|
require "xlsxrb"
|
|
100
77
|
|
|
101
|
-
# Stream row-by-row and cell-by-cell (O(1) memory)
|
|
102
78
|
Xlsxrb.read("large_file.xlsx") do |sheet|
|
|
103
79
|
sheet.each_row do |row|
|
|
104
80
|
row.each_cell do |cell|
|
|
@@ -106,39 +82,14 @@ Xlsxrb.read("large_file.xlsx") do |sheet|
|
|
|
106
82
|
end
|
|
107
83
|
end
|
|
108
84
|
end
|
|
109
|
-
|
|
110
|
-
# Or stream all cells across the sheet directly
|
|
111
|
-
Xlsxrb.read("large_file.xlsx") do |sheet|
|
|
112
|
-
sheet.each_cell do |cell|
|
|
113
|
-
puts "#{cell.ref} = #{cell.value}"
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
85
|
```
|
|
117
86
|
|
|
118
|
-
###
|
|
119
|
-
|
|
120
|
-
`xlsxrb` provides clean, standard Ruby interfaces (`Enumerable`, `Row#to_a`, `sheet["A1"]`) that feel natural to every Ruby developer without learning complex library-specific APIs:
|
|
87
|
+
### In-Memory Building & Modifying
|
|
121
88
|
|
|
122
|
-
####
|
|
89
|
+
#### Creating & Exporting (Rails / Mailers)
|
|
123
90
|
```ruby
|
|
124
91
|
require "xlsxrb"
|
|
125
92
|
|
|
126
|
-
# 1. Read from file path, IO, or raw binary string (O(1) constant memory streaming)
|
|
127
|
-
workbook = Xlsxrb.read("data.xlsx")
|
|
128
|
-
sheet = workbook.sheets.first
|
|
129
|
-
|
|
130
|
-
# 2. Extract sheet data into 2D array of values via standard Enumerable
|
|
131
|
-
matrix = sheet.map(&:to_a) # => [["Name", "Score"], ["Alice", 100], ["Bob", 95]]
|
|
132
|
-
|
|
133
|
-
# 3. Explicitly load into memory for coordinate random access (e.g. sheet["A1"])
|
|
134
|
-
doc_sheet = sheet.load
|
|
135
|
-
doc_sheet["A1"] # => #<Xlsxrb::Elements::Cell value="Name" ...>
|
|
136
|
-
doc_sheet["A1"].value # => "Name"
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
#### Writing & In-Memory Export (Rails & Mailers)
|
|
140
|
-
```ruby
|
|
141
|
-
# Build workbook
|
|
142
93
|
wb = Xlsxrb.build do |b|
|
|
143
94
|
b.sheet("Report") do |s|
|
|
144
95
|
s.row(["Metric", "Value"])
|
|
@@ -146,180 +97,84 @@ wb = Xlsxrb.build do |b|
|
|
|
146
97
|
end
|
|
147
98
|
end
|
|
148
99
|
|
|
149
|
-
# Save
|
|
100
|
+
# Save to file or get binary string for Rails send_data
|
|
150
101
|
Xlsxrb.write("report.xlsx", wb)
|
|
151
|
-
|
|
152
|
-
# Or export to binary string (ideal for Rails send_data & ActionMailer):
|
|
153
102
|
binary_data = Xlsxrb.write(wb)
|
|
154
103
|
```
|
|
155
104
|
|
|
156
|
-
### In-Memory Building & Modifying
|
|
157
|
-
|
|
158
|
-
`xlsxrb` provides a powerful, immutable-by-default API for modifying existing Excel files or building templates in-memory.
|
|
159
|
-
|
|
160
105
|
#### Modifying an Existing File
|
|
161
|
-
You can update specific cells or sheets using the functional `Xlsxrb.modify` API, which yields the parsed `Elements::Workbook`.
|
|
162
|
-
|
|
163
106
|
```ruby
|
|
164
107
|
require "xlsxrb"
|
|
165
108
|
|
|
166
|
-
# Create a template.xlsx for this example
|
|
167
|
-
Xlsxrb.build { |builder| builder.sheet("Invoice") }.write("template.xlsx")
|
|
168
|
-
|
|
169
109
|
Xlsxrb.modify("template.xlsx", "output.xlsx") do |workbook|
|
|
170
110
|
workbook.update_sheet("Invoice") do |sheet|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
sheet = sheet.update_cell("C5", value: Date.today)
|
|
174
|
-
|
|
175
|
-
# Or append new rows
|
|
176
|
-
sheet.with(rows: sheet.rows + [
|
|
177
|
-
Xlsxrb::Elements::Row.new(index: sheet.rows.size, cells: [])
|
|
178
|
-
])
|
|
111
|
+
sheet.update_cell("C4", value: "INV-10042")
|
|
112
|
+
.update_cell("C5", value: Date.today)
|
|
179
113
|
end
|
|
180
114
|
end
|
|
181
115
|
```
|
|
182
116
|
|
|
183
|
-
|
|
184
|
-
|
|
117
|
+
### Password Protection & Encryption ([MS-OFFCRYPTO])
|
|
118
|
+
|
|
119
|
+
Natively supports reading and writing encrypted XLSX files (Standard & Agile Encryption) with zero external C-extensions:
|
|
185
120
|
|
|
186
121
|
```ruby
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
122
|
+
require "xlsxrb"
|
|
123
|
+
|
|
124
|
+
# Write password-protected file
|
|
125
|
+
Xlsxrb.write("confidential.xlsx", password: "SecretPassword123") do |writer|
|
|
126
|
+
writer.sheet("Financials") { |sheet| sheet.row(["Assets", 5_000_000]) }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Read password-protected file
|
|
130
|
+
Xlsxrb.read("confidential.xlsx", password: "SecretPassword123") do |sheet|
|
|
131
|
+
sheet.each_row { |row| puts row.cells.map(&:value) }
|
|
197
132
|
end
|
|
198
133
|
```
|
|
199
134
|
|
|
200
135
|
### IDE Autocompletion & Ruby LSP Support
|
|
201
136
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Whether you use standard descriptive block variable names (`|stream_writer|`, `|sheet|`, `|workbook|`) or short names (`|wb|`, `|s|`), your editor will automatically provide complete method suggestions and parameter hints:
|
|
137
|
+
Includes a native **Ruby LSP Add-on** and full **RBS signatures** for zero-configuration method autocompletion and hover documentation in VS Code and other editors:
|
|
205
138
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
sheet.row(["Product", "Price"], styles: :bold)
|
|
210
|
-
sheet.auto_filter("A1:B100")
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
```
|
|
139
|
+
<p align="center">
|
|
140
|
+
<img src="docs/assets/lsp_autocompletion.png" width="100%" alt="Ruby LSP Autocompletion & Type Signature Hints in VS Code"/>
|
|
141
|
+
</p>
|
|
214
142
|
|
|
215
143
|
## Feature Support & ECMA-376 Compliance
|
|
216
144
|
|
|
217
|
-
`xlsxrb`
|
|
145
|
+
`xlsxrb` supports nearly all major business spreadsheet features:
|
|
146
|
+
* **Layout & Structure**: Formulas, Hyperlinks, Merge Cells, Freeze/Split Panes, Page Setup, Auto Filters, Data Validations, Sheet/Workbook Protection.
|
|
147
|
+
* **Styling & Media**: Rich Text, Cell Styles & Fills, Conditional Formatting (color scales, data bars), Embedded Images, Charts (Line, Bar, Pie, Radar, Scatter).
|
|
218
148
|
|
|
219
|
-
|
|
220
|
-
* Data & Controls: Auto Filters, Data Validations (dropdowns, range limits), Sheet Protection.
|
|
221
|
-
* Formatting & Styling: Rich Text, Cell Tables, Conditional Formatting (color scales, data bars, icon sets).
|
|
222
|
-
* Graphics & Charts: Embedded Images, Shapes & Drawings, Sparklines, Charts (Line, Bar, Pie, Area, Radar, Scatter).
|
|
223
|
-
* Workbook Level: Defined Names, Print Areas, Workbook Protection, and Document Metadata (core, app, custom properties).
|
|
224
|
-
|
|
225
|
-
For detailed specification references and policies, see [SPEC_SOURCES.md](docs/SPEC_SOURCES.md).
|
|
149
|
+
For full details, see [docs/SPEC_SOURCES.md](docs/SPEC_SOURCES.md).
|
|
226
150
|
|
|
227
151
|
## Benchmarks
|
|
228
152
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
### Write Performance (1,000,000 cells)
|
|
232
|
-
|
|
233
|
-
| Library | Model | Write String Storage | Time (Median) | Time (Mean) | Peak Memory | GC Count |
|
|
234
|
-
| ---------------------- | ----------- | -------------------- | ------------- | ----------- | ----------- | -------- |
|
|
235
|
-
| xlsxtream 3.1.0 | Streaming | Inline String | 1.19 s | 1.20 s | 18.2 MB | 1072.0 |
|
|
236
|
-
| xlsxrb (Streaming) | Streaming | SST (Shared) | 1.73 s | 1.65 s | 94.4 MB | 39.0 |
|
|
237
|
-
| fast_excel 0.5.0 (C) | Streaming | SST (Shared) | 1.89 s | 1.89 s | 148.2 MB | 245.0 |
|
|
238
|
-
| xlsxrb (In-Memory) | In-Memory | SST (Shared) | 3.84 s | 3.83 s | 278.3 MB | 32.0 |
|
|
239
|
-
| write_xlsx 1.15.0 | In-Memory | SST (Shared) | 4.32 s | 4.34 s | 201.2 MB | 33.0 |
|
|
240
|
-
| caxlsx 4.5.0 | In-Memory | Inline String | 5.15 s | 5.12 s | 188.6 MB | 23.0 |
|
|
241
|
-
| rubyXL 3.4.38 | In-Memory | Inline String | 38.81 s | 37.82 s | 2186.8 MB | 103.0 |
|
|
242
|
-
|
|
243
|
-
> **Note**: All libraries are evaluated in their **default, out-of-the-box configuration**. Under the same Microsoft Excel-standard Shared String Table (SST) architecture, Pure Ruby `xlsxrb` (Streaming: 1.73s, In-Memory: 3.84s) writes 1,000,000 cells faster than the C-extension `fast_excel` (1.89s) and in-memory gems like `write_xlsx` (4.32s) and `caxlsx` (5.15s).
|
|
244
|
-
|
|
245
|
-
### Read Performance (1,000,000 cells)
|
|
246
|
-
|
|
247
|
-
| Library | Model | Time (Median) | Time (Mean) | Peak Memory | GC Count |
|
|
248
|
-
| ------------------------ | ----------- | ------------- | ----------- | ----------- | -------- |
|
|
249
|
-
| xlsxrb (Streaming) | Streaming | 3.17 s | 3.22 s | 91.4 MB | 43.0 |
|
|
250
|
-
| simple_xlsx_reader 5.1.0 | Streaming | 4.48 s | 4.45 s | 38.5 MB | 1669.0 |
|
|
251
|
-
| xlsxrb (In-Memory) | In-Memory | 5.71 s | 5.89 s | 224.8 MB | 63.0 |
|
|
252
|
-
| creek 2.6.3 | Streaming | 8.14 s | 8.02 s | 834.6 MB | 477.0 |
|
|
253
|
-
| xsv 1.4.1 | Streaming | 14.61 s | 14.50 s | 76.1 MB | 2224.0 |
|
|
254
|
-
| roo 3.0.0 | Streaming | 15.69 s | 13.36 s | 119.7 MB | 441.0 |
|
|
255
|
-
| rubyXL 3.4.38 | In-Memory | 37.13 s | 40.35 s | 2537.6 MB | 146.0 |
|
|
256
|
-
|
|
257
|
-
### Running the Benchmarks Locally (Reproducibility)
|
|
258
|
-
|
|
259
|
-
The benchmark suite leverages [`bundler/inline`](https://bundler.io/v2.5/guides/bundler_in_a_single_file_ruby_script.html) to automatically manage and download all peer ecosystem gems without modifying the project's core `Gemfile` or requiring manual global `gem install` steps. Each library is executed in an isolated subprocess (`Bundler.with_unbundled_env`) across multiple runs with standard business dataset rows (integers, strings, floats, booleans, dates) to ensure clean memory and GC measurements without cross-contamination.
|
|
260
|
-
|
|
261
|
-
To run the complete benchmark suite:
|
|
262
|
-
```bash
|
|
263
|
-
ruby benchmark.rb 100000 10
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Security (Protection against CSV/Excel Injection)
|
|
267
|
-
|
|
268
|
-
Unlike CSV files which lack type definitions and force Excel to guess types (often inadvertently executing strings starting with `=`), `.xlsx` files generated by `xlsxrb` are strictly typed.
|
|
269
|
-
|
|
270
|
-
When you pass a Ruby `String` to `xlsxrb`, it explicitly writes it as a `String` (`t="s"`) into the OOXML file. Therefore, **even if a string starts with `=`, Excel will never evaluate it as a formula**. To write a formula, you must explicitly use `Xlsxrb::Elements::Formula.new`. This design completely mitigates CSV/Formula Injection vulnerabilities by default without requiring additional sanitization.
|
|
271
|
-
|
|
272
|
-
### External Link Updates (`update_links`)
|
|
273
|
-
|
|
274
|
-
As an extra layer of "defense in depth", `xlsxrb` configures the workbook to **never automatically update external links** when opened (`updateLinks="never"`). This is intentionally set to `never` by default to prevent Excel from silently reaching out to external resources or executing DDE (Dynamic Data Exchange) links, which is a known vector for malware.
|
|
275
|
-
|
|
276
|
-
If you absolutely need external links to update automatically, you can explicitly override this (though **it is highly discouraged due to security risks**):
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
Xlsxrb.write("file.xlsx") do |wb|
|
|
280
|
-
# WARNING: Enabling this can expose users to malicious external reference vulnerabilities!
|
|
281
|
-
wb.workbook_property(:update_links, "always")
|
|
282
|
-
# ...
|
|
283
|
-
end
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
## Testing & Quality Assurance
|
|
153
|
+
Benchmark processing 1,000,000 cells (100,000 rows × 10 cols) across popular Ruby gems:
|
|
287
154
|
|
|
288
|
-
|
|
155
|
+
<p align="center">
|
|
156
|
+
<img src="docs/assets/benchmark_results.svg" width="100%" alt="Ruby XLSX Performance Benchmarks"/>
|
|
157
|
+
</p>
|
|
289
158
|
|
|
290
|
-
|
|
291
|
-
* **Round-Trip Testing**: Unit tests verify that every generated sheet can be reliably parsed back by the reader with identical content and styling.
|
|
292
|
-
* **Contract Consistency**: Ensures semantic output consistency between the Streaming (`Xlsxrb.write`) and In-Memory (`Xlsxrb.build`) APIs.
|
|
293
|
-
* **Property-Based Testing (PBT)**: Automatically generates random data to catch edge cases (e.g., huge numbers, special characters) preventing unexpected crashes.
|
|
294
|
-
* **Concurrency Validation**: Thread and Ractor safety checks to guarantee no global variable pollution during parallel execution.
|
|
295
|
-
* **Security & DoS Protection**: Hardened against malicious files, including memory exhaustion (ZIP Bombs) and infinite parsing loops.
|
|
159
|
+
For detailed metrics (peak memory, GC count, mean/median times) and architectural tradeoffs (SST vs. Inline Strings), see [docs/PEER_LIBRARIES.md](docs/PEER_LIBRARIES.md).
|
|
296
160
|
|
|
297
|
-
|
|
298
|
-
* **Official Open XML SDK Validation (E2E)**: Every generated spreadsheet is structurally validated against the official Microsoft Open XML SDK to prevent file corruption warnings in Microsoft Excel.
|
|
299
|
-
* **Visual Regression Testing (VRT)**: Spreadsheets are rendered via a headless LibreOffice Calc engine and compared pixel-by-pixel against visual baselines to catch subtle rendering regressions.
|
|
161
|
+
To reproduce locally: `ruby benchmark.rb 100000 10`
|
|
300
162
|
|
|
301
|
-
|
|
302
|
-
* **Continuous Benchmarking**: Memory usage and processing speeds are profiled in CI on large datasets to prevent performance regressions and OOM leaks.
|
|
303
|
-
* **Runtime Type Validation**: Strong dynamic typing using `RBS::Test` to ensure the library's types are perfectly sound at runtime.
|
|
163
|
+
## Quality Assurance & Testing
|
|
304
164
|
|
|
305
|
-
|
|
165
|
+
Backed by an enterprise-grade QA architecture to guarantee absolute reliability:
|
|
166
|
+
* **Official Microsoft Open XML SDK Validation**: Validates generated OOXML structures against Microsoft's official SDK.
|
|
167
|
+
* **Visual Regression Testing (VRT)**: Headless LibreOffice Calc pixel-by-pixel rendering checks.
|
|
168
|
+
* **Contract & Round-Trip Tests**: Verifies parity between Streaming and In-Memory APIs and round-trip read/write accuracy.
|
|
169
|
+
* **Security & DoS Protection**: Formula injection mitigation and ZIP bomb protection.
|
|
306
170
|
|
|
307
|
-
|
|
171
|
+
For full architectural details, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/QUALITY_ASSURANCE.md](docs/QUALITY_ASSURANCE.md).
|
|
308
172
|
|
|
309
|
-
|
|
173
|
+
## Development & Contributing
|
|
310
174
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
314
|
-
|
|
315
|
-
## Contributing
|
|
316
|
-
|
|
317
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/niku/xlsxrb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md).
|
|
175
|
+
See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for local setup (Dev Container support), testing workflows, and contribution guidelines.
|
|
318
176
|
|
|
319
177
|
## License
|
|
320
178
|
|
|
321
179
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
322
180
|
|
|
323
|
-
## Code of Conduct
|
|
324
|
-
|
|
325
|
-
Everyone interacting in the Xlsxrb project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
|
data/Rakefile
CHANGED
|
@@ -93,8 +93,8 @@ end
|
|
|
93
93
|
Rake::TestTask.new(test: :ensure_reader_fixtures) do |t|
|
|
94
94
|
t.libs << "test"
|
|
95
95
|
t.libs << "lib"
|
|
96
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
|
97
|
-
workers = ENV.fetch("TEST_WORKERS", Etc.nprocessors)
|
|
96
|
+
t.test_files = FileList["test/xlsxrb/**/*_test.rb", "test/*_test.rb", "test/contract/**/*_test.rb"]
|
|
97
|
+
workers = ENV.fetch("TEST_WORKERS", [Etc.nprocessors, 4].min)
|
|
98
98
|
t.options = "--parallel --n-workers=#{workers}"
|
|
99
99
|
end
|
|
100
100
|
|
|
@@ -121,6 +121,24 @@ namespace :test do
|
|
|
121
121
|
t.options = "--parallel --n-workers=#{workers}"
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
+
desc "Run property-based tests (PBT)"
|
|
125
|
+
Rake::TestTask.new(:pbt) do |t|
|
|
126
|
+
t.libs << "test"
|
|
127
|
+
t.libs << "lib"
|
|
128
|
+
t.test_files = FileList["test/pbt/**/*_test.rb"]
|
|
129
|
+
workers = ENV.fetch("TEST_WORKERS", Etc.nprocessors)
|
|
130
|
+
t.options = "--parallel --n-workers=#{workers}"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
desc "Run memory and performance tests"
|
|
134
|
+
Rake::TestTask.new(:performance) do |t|
|
|
135
|
+
t.libs << "test"
|
|
136
|
+
t.libs << "lib"
|
|
137
|
+
t.test_files = FileList["test/performance/**/*_test.rb"]
|
|
138
|
+
end
|
|
139
|
+
desc "Alias for test:performance"
|
|
140
|
+
task perf: :performance
|
|
141
|
+
|
|
124
142
|
Rake::TestTask.new(:e2e) do |t|
|
|
125
143
|
t.libs << "test"
|
|
126
144
|
t.libs << "lib"
|
|
@@ -277,7 +295,8 @@ task :wasm do
|
|
|
277
295
|
# B. Copy necessary standard libraries dynamically from host's $LOAD_PATH
|
|
278
296
|
stdlib_files = [
|
|
279
297
|
"date.rb", "delegate.rb", "forwardable.rb", "securerandom.rb",
|
|
280
|
-
"random/formatter.rb", "set.rb", "tempfile.rb", "tmpdir.rb", "fileutils.rb"
|
|
298
|
+
"random/formatter.rb", "set.rb", "tempfile.rb", "tmpdir.rb", "fileutils.rb",
|
|
299
|
+
"pp.rb", "prettyprint.rb"
|
|
281
300
|
]
|
|
282
301
|
stdlib_files.each do |name|
|
|
283
302
|
path = $LOAD_PATH.find { |p| File.exist?(File.join(p, name)) }
|
data/docs/DEVELOPMENT.md
CHANGED
|
@@ -14,17 +14,25 @@ To run the different tiers of our testing strategy:
|
|
|
14
14
|
```bash
|
|
15
15
|
bundle exec rake test:contract
|
|
16
16
|
```
|
|
17
|
-
3. **
|
|
17
|
+
3. **Property-Based Tests (PBT):**
|
|
18
|
+
```bash
|
|
19
|
+
bundle exec rake test:pbt
|
|
20
|
+
```
|
|
21
|
+
4. **Memory & Performance Tests:**
|
|
22
|
+
```bash
|
|
23
|
+
bundle exec rake test:perf
|
|
24
|
+
```
|
|
25
|
+
5. **Interoperability (E2E) Tests:**
|
|
18
26
|
Requires .NET SDK to be installed (pre-configured in Dev Container).
|
|
19
27
|
```bash
|
|
20
28
|
bundle exec rake test:e2e
|
|
21
29
|
```
|
|
22
|
-
|
|
30
|
+
6. **Visual Regression Tests (VRT):**
|
|
23
31
|
Requires LibreOffice, ImageMagick, and `poppler-utils`.
|
|
24
32
|
```bash
|
|
25
33
|
bundle exec rake test:visual
|
|
26
34
|
```
|
|
27
|
-
|
|
35
|
+
7. **Run All Tests:**
|
|
28
36
|
```bash
|
|
29
37
|
bundle exec rake test
|
|
30
38
|
```
|