fast_xlsx 0.6.0 → 0.7.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 +18 -0
- data/README.md +34 -0
- data/bench/Gemfile.lock +2 -2
- data/ext/fast_xlsx/src/lib.rs +11 -2
- data/lib/fast_xlsx/version.rb +1 -1
- data/lib/fast_xlsx.rb +128 -19
- data/sig/fast_xlsx.rbs +23 -13
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eddcb1a0c063b94222c0a83e15b7a855a83b26674390ee87f59f1938d7007c9f
|
|
4
|
+
data.tar.gz: 5aa9b9a7b6dbfb70bb53364b16fa31bb2c745534aefc9f0631367ed6cf658043
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d854850a2dd9d9b82d8bbbe4568f41336b3e4a89b977ddfcffb8e08a083305222111dded66d33e8f999dfbfae4d1da319e16f40835e83962bb7ac6081686b333
|
|
7
|
+
data.tar.gz: 973794fd6d14e139f8173e0726f0938f61022f5b503d5b761b4abaf089b9e0dd3debec9083248859f9380a64fd6ead89ec2efc8af64296338960b6caed1269df
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.7.0] - 2026-10-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- A Hash of options works wherever a format goes (`write(0, 0, "x", { bold: true })`, `append(row, format: { bold: true })`, table columns, conditional formats, rich strings); equal Hashes share one format.
|
|
8
|
+
- `ws["B2"] = value` and `ws[row, col] = value` write a cell.
|
|
9
|
+
- `Format#to_h` (its options) and `Format#merge` (a new format with more options, from keywords, a Hash or another Format).
|
|
10
|
+
- Formats with the same options are `==`; `eql?` and `hash` follow `Hash#eql?` (so `10` and `10.0` differ there), letting formats serve as Hash keys. Options may have String keys (e.g. parsed from JSON or YAML); the same option as a String and a Symbol raises `ArgumentError`.
|
|
11
|
+
- `Workbook.new` and `Workbook#add_worksheet` yield the new object to a block.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- A `Format` is frozen, and keeps frozen copies of its option values: one is shared wherever the same options are used.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- `Workbook.new { |wb| ... }` ignored its block.
|
|
20
|
+
|
|
3
21
|
## [0.6.0] - 2026-10-02
|
|
4
22
|
|
|
5
23
|
### Added
|
data/README.md
CHANGED
|
@@ -19,11 +19,36 @@ ws << ["id", "name", "created_at"] # append a row
|
|
|
19
19
|
ws.concat(records.map { |r| [r.id, r.name, r.created_at] }) # append many rows in one call
|
|
20
20
|
ws.write(0, 5, 42) # write a single cell (row, col, value)
|
|
21
21
|
ws.write("F1", 42) # or by its Excel reference
|
|
22
|
+
ws["F1"] = 42 # the same (ws[0, 5] = 42 too)
|
|
22
23
|
|
|
23
24
|
wb.properties(title: "Q3 report", author: "Zac", keywords: "Confidential") # File > Info in Excel
|
|
24
25
|
wb.save("report.xlsx") # or wb.to_xlsx => binary String
|
|
25
26
|
```
|
|
26
27
|
|
|
28
|
+
`Workbook.new` and `add_worksheet` also take a block, which gets the new object:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
wb = FastXlsx::Workbook.new do |wb|
|
|
32
|
+
wb.add_worksheet("Users") do |ws|
|
|
33
|
+
ws.append(%w[id name email], format: { bold: true })
|
|
34
|
+
User.find_each { |u| ws << u.attributes.values_at("id", "name", "email") } # a Hash's values in column order
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Rails
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
def export
|
|
43
|
+
wb = FastXlsx::Workbook.new(memory: :constant) # large exports: memory stays flat
|
|
44
|
+
ws = wb.add_worksheet("Orders")
|
|
45
|
+
ws.append(["Order", "Customer", "Total", "Placed at"], format: { bold: true })
|
|
46
|
+
Order.includes(:customer).find_each { |o| ws << [o.number, o.customer.name, o.total, o.created_at] }
|
|
47
|
+
send_data wb.to_xlsx, filename: "orders.xlsx",
|
|
48
|
+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
27
52
|
### Memory modes
|
|
28
53
|
|
|
29
54
|
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:
|
|
@@ -80,6 +105,15 @@ ws.write(1, 2, Date.today, date) # format one cell
|
|
|
80
105
|
| `locked` | `false` keeps the cell editable on a protected sheet (default `true`) |
|
|
81
106
|
| `hidden` | `true` hides the cell's formula on a protected sheet |
|
|
82
107
|
|
|
108
|
+
A Hash of options works wherever a format goes (equal Hashes share one format), and a format can be extended:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
ws.append(["Total", 1_234], format: { bold: true })
|
|
112
|
+
title = header.merge(font_size: 16) # header's options plus font_size; header.to_h lists them
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
In a loop over many cells, create the `Format` once: building one from a Hash on every call is slower.
|
|
116
|
+
|
|
83
117
|
Per-side borders override `border`. Unknown options and invalid values raise `ArgumentError`.
|
|
84
118
|
|
|
85
119
|
Dates and times are shown as `yyyy-mm-dd` or `yyyy-mm-dd hh:mm:ss`. A format without a `num_format` (yours, a table column's or `column_format`'s) keeps its look and gets that date format added, so `append(row, format: bold)` gives bold dates; a format with a `num_format` is used as is.
|
data/bench/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
fast_xlsx (0.
|
|
4
|
+
fast_xlsx (0.7.0)
|
|
5
5
|
rb_sys (~> 0.9.130)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -77,7 +77,7 @@ DEPENDENCIES
|
|
|
77
77
|
CHECKSUMS
|
|
78
78
|
caxlsx (4.5.0) sha256=e3d98d859f148df05d5462086b5079b523f29c1766b569535f1d68629ce743ff
|
|
79
79
|
fast_excel (0.5.0) sha256=59c418bdcf586a6030798d4e3a9f575742badaceaf7bb90f38eee5feb5bdc478
|
|
80
|
-
fast_xlsx (0.
|
|
80
|
+
fast_xlsx (0.7.0)
|
|
81
81
|
ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
|
|
82
82
|
ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
|
|
83
83
|
ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
|
data/ext/fast_xlsx/src/lib.rs
CHANGED
|
@@ -33,6 +33,7 @@ static ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| fast_xlsx_const(ruby, "Err
|
|
|
33
33
|
static FORMULA: Lazy<RClass> = Lazy::new(|ruby| fast_xlsx_const(ruby, "Formula"));
|
|
34
34
|
static URL: Lazy<RClass> = Lazy::new(|ruby| fast_xlsx_const(ruby, "URL"));
|
|
35
35
|
static RICH_STRING: Lazy<RClass> = Lazy::new(|ruby| fast_xlsx_const(ruby, "RichString"));
|
|
36
|
+
static FORMAT: Lazy<RClass> = Lazy::new(|ruby| fast_xlsx_const(ruby, "Format"));
|
|
36
37
|
|
|
37
38
|
// A row or column outside the sheet is a RangeError, like a negative one
|
|
38
39
|
// (which fails converting to an unsigned number before reaching the writer).
|
|
@@ -1172,8 +1173,16 @@ impl Worksheet {
|
|
|
1172
1173
|
fn write_any(ruby: &Ruby, rb_self: Obj<Self>, args: &[Value]) -> Result<Obj<Self>, Error> {
|
|
1173
1174
|
let index = |i: usize| args.get(i).and_then(|v| Integer::from_value(*v));
|
|
1174
1175
|
if let (3 | 4, Some(row), Some(col)) = (args.len(), index(0), index(1)) {
|
|
1175
|
-
|
|
1176
|
-
|
|
1176
|
+
// A Hash of options becomes a (cached) Format, as in Ruby.
|
|
1177
|
+
let format_arg = match args.get(3) {
|
|
1178
|
+
Some(f) if RHash::from_value(*f).is_some() => Some(
|
|
1179
|
+
ruby.get_inner(&FORMAT)
|
|
1180
|
+
.funcall::<_, _, Value>("_coerce", (*f,))?,
|
|
1181
|
+
),
|
|
1182
|
+
f => f.copied(),
|
|
1183
|
+
};
|
|
1184
|
+
let format = match format_arg {
|
|
1185
|
+
Some(f) => Option::<&Format>::try_convert(f)?,
|
|
1177
1186
|
None => None,
|
|
1178
1187
|
};
|
|
1179
1188
|
Self::write(
|
data/lib/fast_xlsx/version.rb
CHANGED
data/lib/fast_xlsx.rb
CHANGED
|
@@ -74,7 +74,9 @@ module FastXlsx
|
|
|
74
74
|
def initialize(*parts)
|
|
75
75
|
raise ArgumentError, "RichString needs at least one segment" if parts.empty?
|
|
76
76
|
|
|
77
|
-
@segments = parts.map
|
|
77
|
+
@segments = parts.map do |part|
|
|
78
|
+
part.is_a?(Array) ? [part[0].to_s, Format._coerce(part[1])] : [part.to_s, nil]
|
|
79
|
+
end.freeze
|
|
78
80
|
end
|
|
79
81
|
end
|
|
80
82
|
|
|
@@ -111,21 +113,33 @@ module FastXlsx
|
|
|
111
113
|
# must be filled top to bottom: :constant stores strings inline (memory
|
|
112
114
|
# stays flat), :low keeps Excel's shared string table (memory grows with
|
|
113
115
|
# the number of unique strings).
|
|
116
|
+
# @yieldparam workbook [Workbook] when a block is given
|
|
117
|
+
# @return [Workbook]
|
|
114
118
|
# @raise [ArgumentError] for an unknown mode
|
|
119
|
+
# @example
|
|
120
|
+
# wb = FastXlsx::Workbook.new do |wb|
|
|
121
|
+
# wb.add_worksheet("Report") { |ws| ws << %w[id name] }
|
|
122
|
+
# end
|
|
115
123
|
def self.new(memory: :standard)
|
|
116
124
|
unless MEMORY_MODES.include?(memory)
|
|
117
125
|
raise ArgumentError, "unknown memory mode #{memory.inspect} (expected one of #{MEMORY_MODES.join(", ")})"
|
|
118
126
|
end
|
|
119
127
|
|
|
120
|
-
_new(memory == :constant, memory == :low)
|
|
128
|
+
workbook = _new(memory == :constant, memory == :low)
|
|
129
|
+
yield workbook if block_given?
|
|
130
|
+
workbook
|
|
121
131
|
end
|
|
122
132
|
|
|
123
133
|
# @param name [String, nil] nil takes the first free "SheetN"
|
|
134
|
+
# @yieldparam worksheet [Worksheet] when a block is given
|
|
124
135
|
# @return [Worksheet]
|
|
125
136
|
# @raise [FastXlsx::Error] for an invalid name or one already used
|
|
126
137
|
# (names ignore case)
|
|
127
138
|
def add_worksheet(name = nil)
|
|
128
|
-
_add_worksheet(name)
|
|
139
|
+
worksheet = _add_worksheet(name)
|
|
140
|
+
worksheets << worksheet
|
|
141
|
+
yield worksheet if block_given?
|
|
142
|
+
worksheet
|
|
129
143
|
end
|
|
130
144
|
|
|
131
145
|
# The same Worksheet objects add_worksheet returned, so their append
|
|
@@ -175,7 +189,7 @@ module FastXlsx
|
|
|
175
189
|
# @param value [Numeric, String, Time, Date, DateTime, true, false, nil,
|
|
176
190
|
# Formula, URL, RichString, #to_s] dates get yyyy-mm-dd (hh:mm:ss) unless
|
|
177
191
|
# the format has a num_format
|
|
178
|
-
# @param format [Format, nil]
|
|
192
|
+
# @param format [Format, Hash, nil] a Hash of options works too
|
|
179
193
|
# @return [self]
|
|
180
194
|
#
|
|
181
195
|
# @!method <<(values)
|
|
@@ -243,18 +257,28 @@ module FastXlsx
|
|
|
243
257
|
# wrapper. Other forms come here.
|
|
244
258
|
def _write_ref(*args)
|
|
245
259
|
row, col, (value, format) = CellRange.cell(args, 1..2)
|
|
246
|
-
_write(row, col, value, format)
|
|
260
|
+
_write(row, col, value, Format._coerce(format))
|
|
247
261
|
self
|
|
248
262
|
end
|
|
249
263
|
private :_write_ref
|
|
250
264
|
|
|
265
|
+
# Writes one cell: ws["B2"] = 42 or ws[1, 1] = 42. Use {#write} to give
|
|
266
|
+
# it a format.
|
|
267
|
+
# @overload []=(ref, value)
|
|
268
|
+
# @overload []=(row, col, value)
|
|
269
|
+
def []=(*cell, value)
|
|
270
|
+
row, col = CellRange.cell(cell)
|
|
271
|
+
write(row, col, value)
|
|
272
|
+
value
|
|
273
|
+
end
|
|
274
|
+
|
|
251
275
|
# Appends a row after the last row written.
|
|
252
276
|
# @param values [Array] cell values, as for {#write}
|
|
253
|
-
# @param format [Format, Array<Format, nil>, nil] one for every cell, or
|
|
277
|
+
# @param format [Format, Hash, Array<Format, Hash, nil>, nil] one for every cell, or
|
|
254
278
|
# one per cell
|
|
255
279
|
# @return [self]
|
|
256
280
|
def append(values, format: nil)
|
|
257
|
-
_append(values, format)
|
|
281
|
+
_append(values, format.is_a?(Array) ? format.map { |f| Format._coerce(f) } : Format._coerce(format))
|
|
258
282
|
end
|
|
259
283
|
|
|
260
284
|
# @param columns [Integer, Range<Integer>]
|
|
@@ -291,7 +315,7 @@ module FastXlsx
|
|
|
291
315
|
# @raise [FastXlsx::Error] when it overlaps an earlier merge
|
|
292
316
|
def merge_range(*args)
|
|
293
317
|
range, (value, format) = CellRange.split(args, 1..2)
|
|
294
|
-
_merge_range(*range, value, format)
|
|
318
|
+
_merge_range(*range, value, Format._coerce(format))
|
|
295
319
|
end
|
|
296
320
|
|
|
297
321
|
# Highlights cells in the range by rule; see the README for each type's
|
|
@@ -300,11 +324,12 @@ module FastXlsx
|
|
|
300
324
|
# @param type [Symbol] :cell, :text, :formula, :data_bar or :color_scale
|
|
301
325
|
# @option options [Symbol] :criteria e.g. :>, :between, :contains
|
|
302
326
|
# @option options [Numeric, String, Array] :value
|
|
303
|
-
# @option options [Format] :format
|
|
327
|
+
# @option options [Format, Hash] :format
|
|
304
328
|
# @option options [Integer] :colors 2 or 3 (:color_scale)
|
|
305
329
|
# @return [self]
|
|
306
|
-
def conditional_format(*range, type:, **)
|
|
307
|
-
|
|
330
|
+
def conditional_format(*range, type:, **options)
|
|
331
|
+
options[:format] = Format._coerce(options[:format]) if options.key?(:format)
|
|
332
|
+
_conditional_format(*CellRange.split(range).first, { type: type, **options })
|
|
308
333
|
end
|
|
309
334
|
|
|
310
335
|
# Restricts what can be entered in the range; see the README.
|
|
@@ -364,8 +389,13 @@ module FastXlsx
|
|
|
364
389
|
# @option options [String] :name
|
|
365
390
|
# @option options [Boolean] :total_row, :banded_rows, :autofilter
|
|
366
391
|
# @return [self]
|
|
367
|
-
def add_table(*range, **)
|
|
368
|
-
|
|
392
|
+
def add_table(*range, **options)
|
|
393
|
+
if options[:columns].is_a?(Array)
|
|
394
|
+
options[:columns] = options[:columns].map do |column|
|
|
395
|
+
column.is_a?(Hash) && column.key?(:format) ? column.merge(format: Format._coerce(column[:format])) : column
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
_add_table(*CellRange.split(range).first, options)
|
|
369
399
|
end
|
|
370
400
|
|
|
371
401
|
# Printed page header, using Excel codes such as "&CPage &P of &N".
|
|
@@ -506,20 +536,20 @@ module FastXlsx
|
|
|
506
536
|
# may combine them or show the row's. Each row is kept (about 1 KB) until
|
|
507
537
|
# saving, so style a whole sheet with {#column_format}.
|
|
508
538
|
# @param rows [Integer, Range<Integer>]
|
|
509
|
-
# @param format [Format]
|
|
539
|
+
# @param format [Format, Hash]
|
|
510
540
|
# @return [self]
|
|
511
541
|
# @raise [FastXlsx::Error] in :constant / :low mode, for rows already on disk
|
|
512
542
|
def row_format(rows, format)
|
|
513
|
-
_row_format(*CellRange.bounds(rows), format)
|
|
543
|
+
_row_format(*CellRange.bounds(rows), Format._coerce(format))
|
|
514
544
|
self
|
|
515
545
|
end
|
|
516
546
|
|
|
517
547
|
# Default format for cells in these columns that are written without one.
|
|
518
548
|
# @param columns [Integer, Range<Integer>]
|
|
519
|
-
# @param format [Format]
|
|
549
|
+
# @param format [Format, Hash]
|
|
520
550
|
# @return [self]
|
|
521
551
|
def column_format(columns, format)
|
|
522
|
-
_column_format(*CellRange.bounds(columns), format)
|
|
552
|
+
_column_format(*CellRange.bounds(columns), Format._coerce(format))
|
|
523
553
|
self
|
|
524
554
|
end
|
|
525
555
|
end
|
|
@@ -633,9 +663,88 @@ module FastXlsx
|
|
|
633
663
|
# border_color:, locked:, hidden:
|
|
634
664
|
# @raise [ArgumentError] for an unknown option or an invalid value
|
|
635
665
|
def self.new(**options)
|
|
666
|
+
options = _options(options)
|
|
636
667
|
# Apply border: first so border_left: etc. override it whatever the order.
|
|
637
|
-
|
|
638
|
-
_new(
|
|
668
|
+
ordered = options.key?(:border) ? { border: options[:border], **options.except(:border) } : options
|
|
669
|
+
format = _new(ordered)
|
|
670
|
+
format.instance_variable_set(:@options, options)
|
|
671
|
+
format.freeze
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
# Options with Symbol keys (String ones come from JSON or YAML) and frozen
|
|
675
|
+
# copies of the values, so the format can't change behind the file's back.
|
|
676
|
+
# @api private
|
|
677
|
+
def self._options(given)
|
|
678
|
+
options = given.to_h { |key, value| [key.to_sym, _frozen(value)] }.freeze
|
|
679
|
+
return options if options.size == given.size
|
|
680
|
+
|
|
681
|
+
twice = given.keys.map(&:to_sym).tally.select { |_, count| count > 1 }.keys
|
|
682
|
+
raise ArgumentError, "format option given twice (as a String and a Symbol): #{twice.join(", ")}"
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# @api private
|
|
686
|
+
def self._frozen(value)
|
|
687
|
+
value.frozen? ? value : value.dup.freeze
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
# The options this format was created with.
|
|
691
|
+
# @return [Hash{Symbol => Object}] frozen
|
|
692
|
+
def to_h
|
|
693
|
+
@options
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# Formats with the same options are equal; 10 and 10.0 count as the same.
|
|
697
|
+
# @return [Boolean]
|
|
698
|
+
def ==(other)
|
|
699
|
+
other.is_a?(Format) && to_h == other.to_h
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# Equal as Hash keys: like Hash#eql?, 10 and 10.0 differ, matching {#hash}.
|
|
703
|
+
# @return [Boolean]
|
|
704
|
+
def eql?(other)
|
|
705
|
+
other.is_a?(Format) && to_h.eql?(other.to_h)
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
# @return [Integer]
|
|
709
|
+
def hash
|
|
710
|
+
[Format, to_h].hash
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
# A new format with these options added to (or replacing) this one's.
|
|
714
|
+
# @param other [Format, Hash, nil] options to add, like Hash#merge
|
|
715
|
+
# @example
|
|
716
|
+
# title = header.merge(font_size: 16)
|
|
717
|
+
# bold_money = bold.merge(money)
|
|
718
|
+
# @return [Format]
|
|
719
|
+
# @raise [TypeError] when other has no #to_h
|
|
720
|
+
def merge(other = nil, **)
|
|
721
|
+
unless other.respond_to?(:to_h)
|
|
722
|
+
raise TypeError,
|
|
723
|
+
"can't merge #{other.class} into a Format: use a Hash or a Format"
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
Format.new(**to_h, **other.to_h, **)
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
# Distinct Hashes kept by {._coerce}; past this, the oldest is dropped.
|
|
730
|
+
CACHE_SIZE = 1_024
|
|
731
|
+
|
|
732
|
+
# A Format for a Hash of options; equal Hashes share one, so a Hash written
|
|
733
|
+
# with every row costs about as much as a Format made once. Other values
|
|
734
|
+
# are returned as they are. An invalid Hash raises and isn't kept.
|
|
735
|
+
# @api private
|
|
736
|
+
def self._coerce(format)
|
|
737
|
+
return format unless format.is_a?(Hash)
|
|
738
|
+
|
|
739
|
+
# ponytail: evicts the oldest entry, not the least used one, since
|
|
740
|
+
# moving each hit costs every write; a Hash still in use is rebuilt at
|
|
741
|
+
# worst once per CACHE_SIZE new Hashes.
|
|
742
|
+
cache = (@cache ||= {})
|
|
743
|
+
cache.fetch(format) do
|
|
744
|
+
coerced = new(**format)
|
|
745
|
+
cache.shift if cache.size >= CACHE_SIZE
|
|
746
|
+
cache[format.dup.freeze] = coerced
|
|
747
|
+
end
|
|
639
748
|
end
|
|
640
749
|
end
|
|
641
750
|
end
|
data/sig/fast_xlsx.rbs
CHANGED
|
@@ -8,11 +8,14 @@ module FastXlsx
|
|
|
8
8
|
def read: () -> String
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
# A Format, or a Hash of its options.
|
|
12
|
+
type format = Format | Hash[Symbol | String, untyped]
|
|
13
|
+
|
|
11
14
|
type cell = Numeric | String | Time | Date | Formula | URL | RichString | bool | nil | _ToS
|
|
12
15
|
|
|
13
16
|
class RichString
|
|
14
17
|
attr_reader segments: Array[[String, Format?]]
|
|
15
|
-
def initialize: (*(_ToS | [_ToS,
|
|
18
|
+
def initialize: (*(_ToS | [_ToS, format?]) parts) -> void
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
class Formula
|
|
@@ -28,8 +31,8 @@ module FastXlsx
|
|
|
28
31
|
|
|
29
32
|
class Workbook
|
|
30
33
|
MEMORY_MODES: Array[Symbol]
|
|
31
|
-
def self.new: (?memory: :standard | :constant | :low) -> Workbook
|
|
32
|
-
def add_worksheet: (?String? name) -> Worksheet
|
|
34
|
+
def self.new: (?memory: :standard | :constant | :low) ?{ (Workbook) -> void } -> Workbook
|
|
35
|
+
def add_worksheet: (?String? name) ?{ (Worksheet) -> void } -> Worksheet
|
|
33
36
|
def worksheets: () -> Array[Worksheet]
|
|
34
37
|
def worksheet: (String name) -> Worksheet?
|
|
35
38
|
def properties: (?title: String, ?subject: String, ?author: String, ?manager: String,
|
|
@@ -53,18 +56,25 @@ module FastXlsx
|
|
|
53
56
|
?rotation: Integer, ?indent: Integer,
|
|
54
57
|
?border: border, ?border_left: border, ?border_right: border,
|
|
55
58
|
?border_top: border, ?border_bottom: border, ?border_color: color) -> Format
|
|
59
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
60
|
+
def merge: (?format? other, **untyped options) -> Format
|
|
61
|
+
def ==: (untyped other) -> bool
|
|
62
|
+
def eql?: (untyped other) -> bool
|
|
63
|
+
def hash: () -> Integer
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
class Worksheet
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
def
|
|
67
|
+
def []=: (String cell, cell value) -> cell
|
|
68
|
+
| (Integer row, Integer col, cell value) -> cell
|
|
69
|
+
def write: (Integer row, Integer col, cell value, ?format? format) -> self
|
|
70
|
+
| (String cell, cell value, ?format? format) -> self
|
|
71
|
+
def append: (Array[cell] values, ?format: format? | Array[format?]) -> self
|
|
62
72
|
def <<: (Array[cell] row) -> self
|
|
63
73
|
def concat: (Array[Array[cell]] rows) -> self
|
|
64
74
|
def column_width: (Integer | Range[Integer] columns, Numeric width) -> self
|
|
65
75
|
def hide_rows: (Integer | Range[Integer] rows) -> self
|
|
66
76
|
def hide_columns: (Integer | Range[Integer] columns) -> self
|
|
67
|
-
def row_format: (Integer | Range[Integer] rows,
|
|
77
|
+
def row_format: (Integer | Range[Integer] rows, format format) -> self
|
|
68
78
|
def default_row_height: (Numeric height) -> self
|
|
69
79
|
def selection: (*cell_range range) -> self
|
|
70
80
|
def top_left_cell: (Integer row, Integer col) -> self
|
|
@@ -74,7 +84,7 @@ module FastXlsx
|
|
|
74
84
|
| :inconsistent_column_formula
|
|
75
85
|
def ignore_error: (*cell_range | ignorable_error args) -> self
|
|
76
86
|
def unprotect_range: (*cell_range range, ?name: String?, ?password: String?) -> self
|
|
77
|
-
def column_format: (Integer | Range[Integer] columns,
|
|
87
|
+
def column_format: (Integer | Range[Integer] columns, format format) -> self
|
|
78
88
|
def autofit: () -> self
|
|
79
89
|
# A cell range: four 0-based numbers, an Excel reference ("A1:D10"), or
|
|
80
90
|
# rows and columns as Integers or Ranges. Methods with options take it as
|
|
@@ -107,13 +117,13 @@ module FastXlsx
|
|
|
107
117
|
def group_rows: (Integer | Range[Integer] rows, ?collapsed: bool) -> self
|
|
108
118
|
def group_columns: (Integer | Range[Integer] columns, ?collapsed: bool) -> self
|
|
109
119
|
def protect: (?password: String?, ?allow: protect_action | Array[protect_action] | nil) -> self
|
|
110
|
-
def merge_range: (Integer first_row, Integer first_col, Integer last_row, Integer last_col, cell value, ?
|
|
111
|
-
| (String range, cell value, ?
|
|
112
|
-
| (indexes rows, indexes cols, cell value, ?
|
|
120
|
+
def merge_range: (Integer first_row, Integer first_col, Integer last_row, Integer last_col, cell value, ?format? format) -> self
|
|
121
|
+
| (String range, cell value, ?format? format) -> self
|
|
122
|
+
| (indexes rows, indexes cols, cell value, ?format? format) -> self
|
|
113
123
|
def conditional_format: (*cell_range range,
|
|
114
124
|
type: :cell | :text | :formula | :data_bar | :color_scale,
|
|
115
125
|
?criteria: Symbol, ?value: Numeric | String | Array[Numeric | String],
|
|
116
|
-
?format:
|
|
126
|
+
?format: format, ?colors: 2 | 3) -> self
|
|
117
127
|
def data_validation: (*cell_range range,
|
|
118
128
|
type: :list | :whole_number | :decimal | :text_length,
|
|
119
129
|
?criteria: Symbol, ?value: Numeric | String | Array[Numeric | String],
|
|
@@ -133,7 +143,7 @@ module FastXlsx
|
|
|
133
143
|
| (String cell, type: Symbol, series: Array[chart_series],
|
|
134
144
|
?title: String, ?x_axis: String, ?y_axis: String,
|
|
135
145
|
?width: Integer, ?height: Integer) -> self
|
|
136
|
-
type table_column = String | { header: String, ?total: Symbol, ?total_label: String, ?format:
|
|
146
|
+
type table_column = String | { header: String, ?total: Symbol, ?total_label: String, ?format: format }
|
|
137
147
|
|
|
138
148
|
def add_table: (*cell_range range,
|
|
139
149
|
?columns: Array[table_column], ?style: Symbol, ?name: String,
|