fast_xlsx 0.8.1 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Cargo.lock +25 -6
- data/README.md +41 -18
- data/SECURITY.md +26 -0
- data/docs/MIGRATING_FROM_FAST_EXCEL.md +1 -1
- data/ext/fast_xlsx/Cargo.toml +1 -1
- data/ext/fast_xlsx/src/lib.rs +2 -2
- data/lib/fast_xlsx/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0642c1f4069d2b6537757d873ed2e591ddd3c7dab25f4bd1c867fad4fe8f0ead
|
|
4
|
+
data.tar.gz: d0b7696cc3ee573ec02821a3b210ef34ce68fb821c83666f2fa0938c8bbabc16
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40fbd10f5ad43e8fc7a6bedccdc68cee4a324fa5ae9586dd7e9281272c6c2b1a465049c53fd67f042c9dffe00ca7c21c1512d4e1ffcbd01e2ea5ce52dc658938
|
|
7
|
+
data.tar.gz: 5a6ca0279e195f3b109d23f0bf1afbed5680c6f6fd844aa0e30411468e1f8d7203680d30bba838cdb15b2dfac1f9c170a9b9576ed5db0650439c331fd2ff11be
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.0.0] - 2026-10-18
|
|
4
|
+
|
|
5
|
+
The first stable release, with the same API as 0.8.1. From now on fast_xlsx follows Semantic Versioning: no breaking change before 2.0, and anything to be removed first prints a deprecation warning for at least one minor release.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Updated rust_xlsxwriter to 0.100, which brings zip 9.0 (RUSTSEC-2026-0367, -0368 and -0369; those affect extracting archives, which fast_xlsx never does).
|
|
10
|
+
- In `memory: :constant` and `:low` mode, a temporary file that can't be created (e.g. an unwritable temp directory) raises `FastXlsx::Error` instead of crashing the process. The file is also created only when the first row is flushed, not by `add_worksheet`.
|
|
11
|
+
|
|
3
12
|
## [0.8.1] - 2026-10-03
|
|
4
13
|
|
|
5
14
|
### Fixed
|
data/Cargo.lock
CHANGED
|
@@ -56,6 +56,12 @@ version = "1.0.5"
|
|
|
56
56
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
57
|
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
|
58
58
|
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "cfg_aliases"
|
|
61
|
+
version = "0.2.2"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
|
|
64
|
+
|
|
59
65
|
[[package]]
|
|
60
66
|
name = "clang-sys"
|
|
61
67
|
version = "1.9.1"
|
|
@@ -201,9 +207,9 @@ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
|
|
201
207
|
|
|
202
208
|
[[package]]
|
|
203
209
|
name = "magnus"
|
|
204
|
-
version = "0.9.
|
|
210
|
+
version = "0.9.2"
|
|
205
211
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
206
|
-
checksum = "
|
|
212
|
+
checksum = "b9068710e00d21be762edca59b9c4c8946dbdb29820d4f76ad559afea07982c9"
|
|
207
213
|
dependencies = [
|
|
208
214
|
"magnus-macros",
|
|
209
215
|
"rb-sys",
|
|
@@ -234,6 +240,18 @@ version = "0.2.1"
|
|
|
234
240
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
235
241
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|
236
242
|
|
|
243
|
+
[[package]]
|
|
244
|
+
name = "nix"
|
|
245
|
+
version = "0.31.3"
|
|
246
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
247
|
+
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
|
248
|
+
dependencies = [
|
|
249
|
+
"bitflags",
|
|
250
|
+
"cfg-if",
|
|
251
|
+
"cfg_aliases",
|
|
252
|
+
"libc",
|
|
253
|
+
]
|
|
254
|
+
|
|
237
255
|
[[package]]
|
|
238
256
|
name = "nom"
|
|
239
257
|
version = "7.1.3"
|
|
@@ -335,9 +353,9 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
|
335
353
|
|
|
336
354
|
[[package]]
|
|
337
355
|
name = "rust_xlsxwriter"
|
|
338
|
-
version = "0.
|
|
356
|
+
version = "0.100.0"
|
|
339
357
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
340
|
-
checksum = "
|
|
358
|
+
checksum = "c44904ffb6c57b148ffcc8ce3011c28bc49b97e1a8343b2d632283cd566e1393"
|
|
341
359
|
dependencies = [
|
|
342
360
|
"tempfile",
|
|
343
361
|
"zip",
|
|
@@ -439,14 +457,15 @@ dependencies = [
|
|
|
439
457
|
|
|
440
458
|
[[package]]
|
|
441
459
|
name = "zip"
|
|
442
|
-
version = "
|
|
460
|
+
version = "9.0.5"
|
|
443
461
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
444
|
-
checksum = "
|
|
462
|
+
checksum = "6e04976da629d3b04204ba8105b396a193af8ad2583b2c79bfb3ab8af580a11e"
|
|
445
463
|
dependencies = [
|
|
446
464
|
"crc32fast",
|
|
447
465
|
"flate2",
|
|
448
466
|
"indexmap",
|
|
449
467
|
"memchr",
|
|
468
|
+
"nix",
|
|
450
469
|
"typed-path",
|
|
451
470
|
"zopfli",
|
|
452
471
|
]
|
data/README.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# fast_xlsx
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://rubygems.org/gems/fast_xlsx)
|
|
4
6
|
[](https://codecov.io/gh/7a6163/fast_xlsx)
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
Write Excel `.xlsx` files from Ruby with a Rust engine ([rust_xlsxwriter](https://github.com/jmcnamara/rust_xlsxwriter), via [magnus](https://github.com/matsadler/magnus)): 20,000 rows in under 100 ms, with memory that stays flat for large exports.
|
|
7
9
|
|
|
8
|
-
> **Status:
|
|
10
|
+
> **Status: stable.** Since 1.0 the API follows [Semantic Versioning](https://semver.org): no breaking change before 2.0.
|
|
9
11
|
|
|
10
|
-
**Roadmap:** [1.
|
|
12
|
+
**Roadmap:** [1.x](https://github.com/7a6163/fast_xlsx/milestone/2) (planned features)
|
|
11
13
|
|
|
12
|
-
**Contents:** [Installation](#installation) · [Getting started](#getting-started) · [Guide](#guide) · [Performance](#performance) · [Support and versioning](#support-and-versioning) · [Development](#development)
|
|
14
|
+
**Contents:** [Installation](#installation) · [Getting started](#getting-started) · [Guide](#guide) · [Performance](#performance) · [Support and versioning](#support-and-versioning) · [Development](#development) · [Credits](#credits)
|
|
13
15
|
|
|
14
16
|
## Installation
|
|
15
17
|
|
|
@@ -81,7 +83,7 @@ The [Guide](#guide) below covers every feature; the [API docs](https://www.rubyd
|
|
|
81
83
|
|
|
82
84
|
## Guide
|
|
83
85
|
|
|
84
|
-
[Writing cells](#writing-cells) · [Rails](#rails) · [Memory modes](#memory-modes) · [Formats](#formats) · [Columns and filters](#columns-and-filters) · [Sheet view and printing](#sheet-view-and-printing) · [Cell ranges](#cell-ranges) · [Layout](#layout) · [Outline groups](#outline-groups) · [Defined names](#defined-names) · [Protection](#protection) · [Conditional formats](#conditional-formats) · [Data validation](#data-validation) · [Comments](#comments) · [Images](#images) · [Tables](#tables) · [Charts](#charts) · [Errors](#errors)
|
|
86
|
+
[Writing cells](#writing-cells) · [Rails](#rails) · [Polars](#polars) · [Memory modes](#memory-modes) · [Formats](#formats) · [Columns and filters](#columns-and-filters) · [Sheet view and printing](#sheet-view-and-printing) · [Cell ranges](#cell-ranges) · [Layout](#layout) · [Outline groups](#outline-groups) · [Defined names](#defined-names) · [Protection](#protection) · [Conditional formats](#conditional-formats) · [Data validation](#data-validation) · [Comments](#comments) · [Images](#images) · [Tables](#tables) · [Charts](#charts) · [Errors](#errors)
|
|
85
87
|
|
|
86
88
|
### Writing cells
|
|
87
89
|
|
|
@@ -126,6 +128,23 @@ def export
|
|
|
126
128
|
end
|
|
127
129
|
```
|
|
128
130
|
|
|
131
|
+
### Polars
|
|
132
|
+
|
|
133
|
+
To write a [ruby-polars](https://github.com/ankane/ruby-polars) DataFrame (gem `polars-df`), hand its column names and rows to fast_xlsx:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
df = Polars.read_csv("sales.csv")
|
|
137
|
+
summary = df.group_by("region").agg(Polars.col("amount").sum)
|
|
138
|
+
|
|
139
|
+
wb = FastXlsx::Workbook.new(memory: :constant)
|
|
140
|
+
ws = wb.add_worksheet("By region")
|
|
141
|
+
ws.append(summary.columns, format: { bold: true })
|
|
142
|
+
summary.iter_rows { |row| ws << row } # in batches of 512 rows, so memory stays flat
|
|
143
|
+
wb.save("by_region.xlsx")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Polars `Date` and `Datetime` columns become dates (a `Datetime` with a time zone keeps its local time), `Decimal` numbers, and nulls empty cells.
|
|
147
|
+
|
|
129
148
|
### Memory modes
|
|
130
149
|
|
|
131
150
|
By default every cell stays in memory until the file is saved. For large exports, two modes write each finished row to a temp file instead:
|
|
@@ -402,15 +421,15 @@ Apple Silicon, Ruby 4.0.5. Each library uses its own idiomatic row-append API; x
|
|
|
402
421
|
|
|
403
422
|
| Library | Time | vs fastest | Ruby objects allocated |
|
|
404
423
|
|---|---:|---:|---:|
|
|
405
|
-
| **fast_xlsx** (`memory: :constant`) | **
|
|
406
|
-
| **fast_xlsx** (`memory: :low`) | **
|
|
407
|
-
| **fast_xlsx** | **
|
|
408
|
-
| [xlsxtream](https://github.com/felixbuenemann/xlsxtream) 3.1 |
|
|
409
|
-
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 (constant_memory) |
|
|
410
|
-
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 | 238 ms | 3.
|
|
411
|
-
| [write_xlsx](https://github.com/cxn03651/write_xlsx) 1.15 |
|
|
412
|
-
| [caxlsx](https://github.com/caxlsx/caxlsx) 4.5 |
|
|
413
|
-
| [rubyXL](https://github.com/weshatheleopard/rubyXL) 3.4 |
|
|
424
|
+
| **fast_xlsx** (`memory: :constant`) | **75 ms** | 1.0x | 7 |
|
|
425
|
+
| **fast_xlsx** (`memory: :low`) | **81 ms** | 1.1x | 7 |
|
|
426
|
+
| **fast_xlsx** | **85 ms** | 1.1x | 10 |
|
|
427
|
+
| [xlsxtream](https://github.com/felixbuenemann/xlsxtream) 3.1 | 173 ms | 2.3x | 561,740 |
|
|
428
|
+
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 (constant_memory) | 204 ms | 2.7x | 20,079 |
|
|
429
|
+
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 | 238 ms | 3.2x | 320,076 |
|
|
430
|
+
| [write_xlsx](https://github.com/cxn03651/write_xlsx) 1.15 | 592 ms | 7.9x | 1,483,899 |
|
|
431
|
+
| [caxlsx](https://github.com/caxlsx/caxlsx) 4.5 | 671 ms | 8.9x | 745,122 |
|
|
432
|
+
| [rubyXL](https://github.com/weshatheleopard/rubyXL) 3.4 | 2571 ms | 34.1x | 8,700,448 |
|
|
414
433
|
|
|
415
434
|
All outputs are 689–750 KB.
|
|
416
435
|
|
|
@@ -448,8 +467,8 @@ BUNDLE_GEMFILE=bench/Gemfile /usr/bin/time -l bundle exec ruby bench/memory.rb f
|
|
|
448
467
|
|
|
449
468
|
- **Ruby:** CRuby 3.3 and later. A Ruby version is dropped only after it reaches its end of life, and only in a minor release. JRuby and TruffleRuby are not supported (this is a native extension).
|
|
450
469
|
- **Precompiled gems:** Linux (x86_64 and aarch64, glibc and musl; ARM musl), macOS (arm64 and x86_64) and Windows (x64). Before each release the Linux x86_64, macOS arm64 and Windows gems are installed and loaded on Ruby 3.3, 3.4 and 4.0. Other platforms build from source and need a Rust toolchain.
|
|
451
|
-
- **Versions:**
|
|
452
|
-
- **Deprecations:**
|
|
470
|
+
- **Versions:** [Semantic Versioning](https://semver.org) since 1.0: no breaking API change before 2.0. The [changelog](CHANGELOG.md) lists every change.
|
|
471
|
+
- **Deprecations:** a method or option to be removed first prints a warning (with `warn`, so on by default) for at least one minor release, with the CHANGELOG naming its replacement; it goes only in the next major version.
|
|
453
472
|
- **Output:** `.xlsx` files that open in Excel 2007 and later, LibreOffice and Google Sheets. Updating rust_xlsxwriter can change the bytes of the file but not what it contains.
|
|
454
473
|
|
|
455
474
|
## Development
|
|
@@ -498,6 +517,10 @@ git tag v0.1.0 && git push origin v0.1.0
|
|
|
498
517
|
|
|
499
518
|
The `Build gems` workflow builds the source gem plus precompiled gems for each platform and pushes them to RubyGems (trusted publishing) and GitHub Packages. It refuses to publish if the tag and `VERSION` differ.
|
|
500
519
|
|
|
520
|
+
## Credits
|
|
521
|
+
|
|
522
|
+
Inspired by [fast_excel](https://github.com/Paxa/fast_excel), whose API and approach fast_xlsx builds on, and made possible by [rust_xlsxwriter](https://github.com/jmcnamara/rust_xlsxwriter) and [magnus](https://github.com/matsadler/magnus).
|
|
523
|
+
|
|
501
524
|
## License
|
|
502
525
|
|
|
503
526
|
MIT
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes go into the latest release of the newest minor version. Until 1.0 that is 0.8.x; from 1.0, the latest 1.x.
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 0.8.x | ✅ |
|
|
10
|
+
| < 0.8 | ❌ (upgrade to 0.8.x; the [changelog](CHANGELOG.md) lists every change) |
|
|
11
|
+
|
|
12
|
+
## Reporting a vulnerability
|
|
13
|
+
|
|
14
|
+
Please don't open a public issue. Report it privately through GitHub: on the repository's **Security** tab, choose **Report a vulnerability** ([direct link](https://github.com/7a6163/fast_xlsx/security/advisories/new)).
|
|
15
|
+
|
|
16
|
+
Include what an attacker could do, the fast_xlsx, Ruby and platform versions, and a script or input that shows it, if you have one.
|
|
17
|
+
|
|
18
|
+
What to expect:
|
|
19
|
+
|
|
20
|
+
- An acknowledgement within a week.
|
|
21
|
+
- A fix in a patch release, and a GitHub security advisory crediting you (unless you'd rather not be named), once users can upgrade.
|
|
22
|
+
- If it turns out to be in a dependency (rust_xlsxwriter, magnus, rb-sys, the Rust zip crates), a report to that project as well.
|
|
23
|
+
|
|
24
|
+
## Scope
|
|
25
|
+
|
|
26
|
+
fast_xlsx only writes `.xlsx` files: it doesn't read or parse spreadsheets. Issues of interest include memory-safety bugs in the native extension (a crash or corruption reachable from Ruby), and output that makes Excel or other readers misbehave from data an application would reasonably pass in (cell text, formulas, URLs, images). Formulas and hyperlinks are written as given: an application writing untrusted input as a `FastXlsx::Formula` or `FastXlsx::URL` should validate it first, as with CSV formula injection.
|
|
@@ -4,7 +4,7 @@ fast_xlsx started as a rewrite of [fast_excel](https://github.com/Paxa/fast_exce
|
|
|
4
4
|
|
|
5
5
|
**Why switch**
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- Faster writing (see the [README benchmark](../README.md#speed)), and `memory: :constant` adds about 1 MB however large the file.
|
|
8
8
|
- Precompiled gems for Linux, macOS and Windows: no C compiler or FFI at install.
|
|
9
9
|
- Dates show as dates without a format, every option is checked (a typo raises instead of being ignored), and saving doesn't block other threads.
|
|
10
10
|
- Conditional formats, data validation, tables, charts, images, comments, protection and printing options as Ruby methods.
|
data/ext/fast_xlsx/Cargo.toml
CHANGED
|
@@ -14,7 +14,7 @@ magnus = { version = "0.9", features = ["rb-sys"] }
|
|
|
14
14
|
rb-sys = { version = "0.9", features = ["stable-api-compiled-fallback"] }
|
|
15
15
|
# No "zlib" feature: without it zip compresses with zlib-rs, which saves
|
|
16
16
|
# 20-30% faster than the system zlib (and needs no C library).
|
|
17
|
-
rust_xlsxwriter = { version = "0.
|
|
17
|
+
rust_xlsxwriter = { version = "0.100.0", features = ["constant_memory"] }
|
|
18
18
|
|
|
19
19
|
[build-dependencies]
|
|
20
20
|
rb-sys-env = "0.2.2"
|
data/ext/fast_xlsx/src/lib.rs
CHANGED
|
@@ -237,9 +237,9 @@ impl Workbook {
|
|
|
237
237
|
.unwrap(),
|
|
238
238
|
};
|
|
239
239
|
let ws = if self.low_memory {
|
|
240
|
-
wb.add_worksheet_with_low_memory()
|
|
240
|
+
wb.add_worksheet_with_low_memory().map_err(xerr)?
|
|
241
241
|
} else if self.constant_memory {
|
|
242
|
-
wb.add_worksheet_with_constant_memory()
|
|
242
|
+
wb.add_worksheet_with_constant_memory().map_err(xerr)?
|
|
243
243
|
} else {
|
|
244
244
|
wb.add_worksheet()
|
|
245
245
|
};
|
data/lib/fast_xlsx/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fast_xlsx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zac
|
|
@@ -23,8 +23,9 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 0.9.130
|
|
26
|
-
description: Writes Excel .xlsx files from Ruby through a native Rust extension (rust_xlsxwriter
|
|
27
|
-
|
|
26
|
+
description: 'Writes Excel .xlsx files from Ruby through a native Rust extension (rust_xlsxwriter):
|
|
27
|
+
20,000 rows in under 100 ms, and memory that stays flat for large exports. Formats,
|
|
28
|
+
formulas, tables, charts, conditional formats, data validation and more.'
|
|
28
29
|
email:
|
|
29
30
|
- 579103+7a6163@users.noreply.github.com
|
|
30
31
|
executables: []
|
|
@@ -40,6 +41,7 @@ files:
|
|
|
40
41
|
- LICENSE.txt
|
|
41
42
|
- README.md
|
|
42
43
|
- Rakefile
|
|
44
|
+
- SECURITY.md
|
|
43
45
|
- bench/Gemfile
|
|
44
46
|
- bench/Gemfile.lock
|
|
45
47
|
- bench/compare.rb
|
|
@@ -81,5 +83,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
81
83
|
requirements: []
|
|
82
84
|
rubygems_version: 4.0.10
|
|
83
85
|
specification_version: 4
|
|
84
|
-
summary:
|
|
86
|
+
summary: Excel .xlsx writer for Ruby with a Rust engine
|
|
85
87
|
test_files: []
|