fast_xlsx 0.1.1-aarch64-linux → 0.2.0-aarch64-linux

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b488208df9a12cb0ecea8cc9c6b5243ef98449f6cff4f75ecb3561cc74498beb
4
- data.tar.gz: 8791fe6aebe1aea052c15441e8ff04f0d85c14eac8b8ae3331a9401c372483d0
3
+ metadata.gz: d720de6fee7241e2b2afa2bfe19fd3faa2ae5504c6c8f61bc6b33ac1327ac19b
4
+ data.tar.gz: 37a01d4a4d4b2cfd3ba6a73bf7c07848f96376fa59fe1e0fe30ca97d3dc5a76a
5
5
  SHA512:
6
- metadata.gz: d0da93867c64884c2d0fa745072b758e526189d561180745d332717a423dc3a837f51f444d177189b7be971815d1b0d5a644142d09f7ea5d2abfbf66bf7f8e4c
7
- data.tar.gz: 367ac6560328286927b610fbf930d61fe085197d56b867f77b5eefe3fa3d033a92f9bef9365b235af746675bd0a14d8c41e2f220931950e76bf9837e97a840cc
6
+ metadata.gz: cf27645fa844dd17688ed73a2f2f4fa7aaa61dd3d374d2866614c36b31c71ee2a50307e605d3910820faee7fcea1a8cdbb14c7654204aca888907b29175c771d
7
+ data.tar.gz: c02dd3ec356bc95e5c7c0eec55cd5cbe79abff5edbc30373b665ed783721f765dafdc152411cc3d815e3e163d61b65a2e840b9886f88e979dfa396ec23a03d6f
data/CHANGELOG.md CHANGED
@@ -1,10 +1,40 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2026-10-01
4
+
5
+ ### Added
6
+
7
+ - Range methods (`autofilter`, `merge_range`, `conditional_format`, `data_validation`, `add_table`) also take an Excel reference (`"A1:D10"`) or rows and columns as Integers or Ranges (`0..9, 0..3`).
8
+ - `Workbook#define_name` for workbook-wide and sheet-scoped defined names.
9
+ - `Worksheet#group_rows` and `#group_columns` (outline groups, optionally collapsed); `group_rows` raises in `:constant` / `:low` mode.
10
+ - `Worksheet#protect(password:, allow:)` locks a sheet; `Format` options `locked: false` and `hidden: true` keep cells editable or hide formulas.
11
+
12
+ ### Changed
13
+
14
+ - `column_width`, `column_format` and the other methods that take a Range raise `ArgumentError` for an empty, reversed or endless Range, or one that isn't Integers, instead of a `TypeError` or `RangeError`.
15
+
16
+ ## [0.1.2] - 2026-09-30
17
+
18
+ ### Fixed
19
+
20
+ - Values Excel cannot hold (a string over 32,767 characters, invalid UTF-8, a bad or over-long URL, more than 16,384 columns) raise before anything is written, instead of leaving the row half written.
21
+ - `merge_range` with such a value no longer leaves the range merged; its value also takes its table column's format.
22
+ - An unnamed worksheet takes the first free `SheetN` instead of a name that clashes with one given earlier (which failed only when saving).
23
+ - Cell values and formats are copied out of Ruby objects when a row is converted, so Ruby code run while converting (a `to_s`) cannot change or free them before they are written.
24
+ - Ruby code run while converting a value (a `to_s`, `jd`, ...) that touches the same workbook, or another thread writing to it, no longer deadlocks. A value that fails to convert leaves its row unwritten.
25
+ - In `:constant` / `:low` mode, `merge_range` and `add_table` on rows already written to disk raise `FastXlsx::Error` instead of being dropped; cells beside a tall merge can still be written.
26
+ - Table column formats apply to rows written after `add_table`.
27
+ - Invalid worksheet names raise without leaving an extra sheet behind; names that differ only in case raise at `add_worksheet`, not at save.
28
+ - Strings in other encodings (e.g. Windows-1252) are converted to UTF-8.
29
+ - `data_validation` lists accept numbers and other values (listed by `to_s`).
30
+ - Dates match Excel's 1900 date system (serials before 1900-03-01 were one too high); dates before 1900 raise `ArgumentError`.
31
+ - `Workbook#save` accepts a `Pathname`.
32
+
3
33
  ## [0.1.1] - 2026-09-30
4
34
 
5
35
  ### Fixed
6
36
 
7
- - Precompiled (platform) gems failed to `require`: the extension was only looked up where a source install puts it, not in the per-Ruby-version directory that platform gems use. 0.1.0 is yanked; upgrade to 0.1.1.
37
+ - Precompiled (platform) gems failed to `require`: the extension was only looked up where a source install puts it, not in the per-Ruby-version directory that platform gems use. 0.1.0's platform gems cannot be loaded; upgrade to 0.1.1 (`bundle update fast_xlsx` if your lockfile has 0.1.0).
8
38
 
9
39
  ### Changed
10
40
 
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # FastXlsx
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/fast_xlsx.svg)](https://badge.fury.io/rb/fast_xlsx)
3
4
  [![codecov](https://codecov.io/gh/7a6163/fast_xlsx/graph/badge.svg)](https://codecov.io/gh/7a6163/fast_xlsx)
4
5
 
5
6
  Fast `.xlsx` writer for Ruby, built on [rust_xlsxwriter](https://github.com/jmcnamara/rust_xlsxwriter) via [magnus](https://github.com/matsadler/magnus).
@@ -75,6 +76,8 @@ ws.write(1, 2, Date.today, date) # format one cell
75
76
  | `valign` | `:top`, `:center`, `:bottom` |
76
77
  | `border`, `border_left`, `border_right`, `border_top`, `border_bottom` | `:thin`, `:medium`, `:thick`, `:dashed`, `:dotted`, `:double`, `:hair` |
77
78
  | `border_color` | `"#RRGGBB"` or `0xRRGGBB` |
79
+ | `locked` | `false` keeps the cell editable on a protected sheet (default `true`) |
80
+ | `hidden` | `true` hides the cell's formula on a protected sheet |
78
81
 
79
82
  Per-side borders override `border`. Unknown options and invalid values raise `ArgumentError`.
80
83
 
@@ -85,7 +88,18 @@ ws.column_width(0, 20) # column A, width in characters
85
88
  ws.column_width(1..3, 12) # columns B–D
86
89
  ws.column_format(4, FastXlsx::Format.new(num_format: "#,##0.00")) # default for cells in E written without a format
87
90
  ws.autofit # size other columns to the data written so far; set widths are kept
88
- ws.autofilter(0, 0, 100, 3) # filter buttons on A1:D101 (first_row, first_col, last_row, last_col)
91
+ ws.autofilter("A1:D101") # filter buttons on A1:D101
92
+ ```
93
+
94
+ ### Cell ranges
95
+
96
+ `autofilter`, `merge_range`, `conditional_format`, `data_validation` and `add_table` take a range in any of these styles:
97
+
98
+ ```ruby
99
+ ws.autofilter(0, 0, 100, 3) # four 0-based numbers: first_row, first_col, last_row, last_col
100
+ ws.autofilter("A1:D101") # an Excel reference; "$A$1:$D$101" and a single "B2" work too
101
+ ws.autofilter(0..100, 0..3) # rows and columns, each an Integer or a Range
102
+ ws.merge_range(0, 0..3, "Q3 report", title) # row 1, columns A–D
89
103
  ```
90
104
 
91
105
  `autofit` only sees rows still in memory, so in `:constant` / `:low` memory mode it ignores rows already written to disk; set widths with `column_width` instead.
@@ -103,6 +117,36 @@ ws.page_footer("&L&A", margin: 0.2) # sheet name on the left; margin
103
117
  ws.margins(left: 0.5, top: 1) # other margins keep Excel's defaults
104
118
  ```
105
119
 
120
+ ### Outline groups
121
+
122
+ ```ruby
123
+ ws.group_rows(1..10) # rows 2–11 get an expand/collapse button
124
+ ws.group_rows(1..4) # grouping again nests them (up to 7 levels)
125
+ ws.group_columns(2..3, collapsed: true) # columns C–D, collapsed until expanded
126
+ ```
127
+
128
+ `group_rows` needs `memory: :standard`: in `:constant` / `:low` mode rust_xlsxwriter writes rows without their outline level, so it raises. `group_columns` works in every mode.
129
+
130
+ ### Defined names
131
+
132
+ ```ruby
133
+ wb.define_name("Rate", "=0.96") # workbook-wide; use as =A1*Rate
134
+ wb.define_name("Report!Sales", "=Report!$B$2:$B$13") # only on the Report sheet
135
+ ```
136
+
137
+ Invalid names raise `FastXlsx::Error` right away; duplicate names, and names for a sheet that doesn't exist, raise when saving.
138
+
139
+ ### Protection
140
+
141
+ ```ruby
142
+ input = FastXlsx::Format.new(locked: false)
143
+ ws.write(1, 1, 0, input) # B2 stays editable
144
+ ws.protect # lock everything else
145
+ ws.protect(password: "secret", allow: %i[sort use_autofilter]) # or with a password and allowed actions
146
+ ```
147
+
148
+ `allow:` takes `:format_cells`, `:format_columns`, `:format_rows`, `:insert_columns`, `:insert_rows`, `:insert_links`, `:delete_columns`, `:delete_rows`, `:sort`, `:use_autofilter`, `:use_pivot_tables`, `:edit_scenarios`, `:edit_objects`. The password only stops editing in Excel; it does not encrypt the file.
149
+
106
150
  ### Conditional formats
107
151
 
108
152
  ```ruby
@@ -276,6 +320,15 @@ cargo llvm-cov report --release # or --lcov / --html
276
320
 
277
321
  Rebuild in a clean shell afterwards (`rm -rf tmp && bundle exec rake compile`) so the everyday build is not instrumented.
278
322
 
323
+ Mutation tests ([cargo-mutants](https://mutants.rs)) change the Rust source one small edit at a time and check that a Ruby test fails for each change. `ext/fast_xlsx/tests/ruby_suite.rs` is what connects the two: it rebuilds the extension and runs the Ruby suite, so `cargo test` covers the Ruby tests too.
324
+
325
+ ```bash
326
+ cargo install cargo-mutants
327
+ cargo mutants --file ext/fast_xlsx/src/lib.rs --jobs 4 # ~7 minutes; results in mutants.out/
328
+ ```
329
+
330
+ Mutants listed in `mutants.out/missed.txt` point at behaviour no test checks. The `Mutation tests` workflow runs this weekly and on demand.
331
+
279
332
  ### Releasing
280
333
 
281
334
  First generate the showcase workbook and open it in Excel to check every feature renders (the test suite only inspects the XML):
data/bench/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- fast_xlsx (0.1.1)
4
+ fast_xlsx (0.2.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.1.1)
80
+ fast_xlsx (0.2.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
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/fast_xlsx/3.3/fast_xlsx.so
3
- size: 44830800 bytes
4
- sha256: 0501357e7151376bdc3a5464679ab8df0faeab0754e8654415ccf9f4fe0725ea
3
+ size: 45767576 bytes
4
+ sha256: e8e29ce6d46d1e3b022c1d061e2c07eca978abe4a5d3503d2795bca0ce84b675
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/fast_xlsx/3.4/fast_xlsx.so
3
- size: 44923960 bytes
4
- sha256: 347a7ad14d24178ed3be6238b30dccd6b062a87d8aa478954bb5801908dd60e8
3
+ size: 45867240 bytes
4
+ sha256: bc5389da41e997494b8e1884bab2dfb9274197dae8df36fabcd1c49113ba0e1e
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/fast_xlsx/4.0/fast_xlsx.so
3
- size: 44900144 bytes
4
- sha256: f9ea7790ccca3b7e5a015df8ecc27fd50043455c5664ec0d4dd80908f999c6ca
3
+ size: 45839040 bytes
4
+ sha256: bf7696eb47f0559789124ef275e9a1a3e04219a51769d6f65d65a4ea7ae593b7
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FastXlsx
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/fast_xlsx.rb CHANGED
@@ -84,6 +84,11 @@ module FastXlsx
84
84
  worksheets.find { |ws| ws.name == name }
85
85
  end
86
86
 
87
+ # path: a String, Pathname or anything responding to #to_path.
88
+ def save(path)
89
+ _save(File.path(path))
90
+ end
91
+
87
92
  # Document properties shown in Excel's File > Info: title:, subject:,
88
93
  # author:, manager:, company:, category:, keywords:, comments:, status:.
89
94
  # Later calls add to earlier ones.
@@ -108,11 +113,11 @@ module FastXlsx
108
113
 
109
114
  # columns: a 0-based column index or a Range of them. width is in characters.
110
115
  def column_width(columns, width)
111
- bounds = column_bounds(columns)
112
- _column_width(*bounds, width)
116
+ range = CellRange.bounds(columns)
117
+ _column_width(*range, width)
113
118
  @fixed_widths ||= {}
114
- @fixed_widths.delete(bounds) # re-insert so autofit replays calls in order
115
- @fixed_widths[bounds] = width
119
+ @fixed_widths.delete(range) # re-insert so autofit replays calls in order
120
+ @fixed_widths[range] = width
116
121
  self
117
122
  end
118
123
 
@@ -124,21 +129,32 @@ module FastXlsx
124
129
  self
125
130
  end
126
131
 
132
+ # The methods below take a cell range in any of these styles:
133
+ # (first_row, first_col, last_row, last_col) four 0-based numbers
134
+ # ("A1:D10") or ("B2") an Excel reference
135
+ # (rows, cols) Integers or Ranges, e.g. (0..9, 0..3)
136
+
137
+ # Filter buttons on the range's first row.
138
+ def autofilter(*range)
139
+ _autofilter(*CellRange.split(range).first)
140
+ end
141
+
127
142
  # Merges the range and writes value (any cell type) into its first cell.
128
- def merge_range(first_row, first_col, last_row, last_col, value, format = nil)
129
- _merge_range(first_row, first_col, last_row, last_col, value, format)
143
+ def merge_range(*args)
144
+ range, (value, format) = CellRange.split(args, 1..2)
145
+ _merge_range(*range, value, format)
130
146
  end
131
147
 
132
148
  # Highlights cells in the range by rule. type: :cell, :text, :formula,
133
149
  # :data_bar or :color_scale; see the README for each type's options.
134
- def conditional_format(first_row, first_col, last_row, last_col, type:, **)
135
- _conditional_format(first_row, first_col, last_row, last_col, { type: type, ** })
150
+ def conditional_format(*range, type:, **)
151
+ _conditional_format(*CellRange.split(range).first, { type: type, ** })
136
152
  end
137
153
 
138
154
  # Restricts what can be entered in the range. type: :list, :whole_number,
139
155
  # :decimal or :text_length; see the README for the options.
140
- def data_validation(first_row, first_col, last_row, last_col, type:, **)
141
- _data_validation(first_row, first_col, last_row, last_col, { type: type, ** })
156
+ def data_validation(*range, type:, **)
157
+ _data_validation(*CellRange.split(range).first, { type: type, ** })
142
158
  end
143
159
 
144
160
  # Adds a comment (Excel "note") to a cell.
@@ -165,8 +181,8 @@ module FastXlsx
165
181
  # into an Excel table. columns: header Strings or { header:, total:,
166
182
  # total_label:, format: }; other options: style:, name:, total_row:,
167
183
  # banded_rows:, autofilter:.
168
- def add_table(first_row, first_col, last_row, last_col, **)
169
- _add_table(first_row, first_col, last_row, last_col, { ** })
184
+ def add_table(*range, **)
185
+ _add_table(*CellRange.split(range).first, { ** })
170
186
  end
171
187
 
172
188
  # Printed page header/footer using Excel codes such as "&CPage &P of &N".
@@ -187,18 +203,106 @@ module FastXlsx
187
203
  self
188
204
  end
189
205
 
206
+ # Outline group with an expand/collapse button. rows: a 0-based row index
207
+ # or a Range; grouping rows already grouped nests them (up to 7 levels).
208
+ # collapsed: true hides them until expanded.
209
+ def group_rows(rows, collapsed: false)
210
+ _group_rows(*CellRange.bounds(rows), collapsed)
211
+ end
212
+
213
+ def group_columns(columns, collapsed: false)
214
+ _group_columns(*CellRange.bounds(columns), collapsed)
215
+ end
216
+
217
+ # Locks the sheet against editing. Cells whose format has locked: false
218
+ # stay editable. allow: actions users may still take, any of :format_cells,
219
+ # :format_columns, :format_rows, :insert_columns, :insert_rows,
220
+ # :insert_links, :delete_columns, :delete_rows, :sort, :use_autofilter,
221
+ # :use_pivot_tables, :edit_scenarios, :edit_objects.
222
+ def protect(password: nil, allow: [])
223
+ _protect(password, Array(allow))
224
+ end
225
+
190
226
  # Default format for cells in these columns that are written without one.
191
227
  def column_format(columns, format)
192
- _column_format(*column_bounds(columns), format)
228
+ _column_format(*CellRange.bounds(columns), format)
193
229
  self
194
230
  end
231
+ end
232
+
233
+ # Cell ranges in the styles Worksheet methods accept: four 0-based numbers,
234
+ # an Excel reference ("A1:D10", "B2"), or rows and columns as Integers or
235
+ # Ranges.
236
+ module CellRange
237
+ REF = /\A\$?([A-Za-z]{1,3})\$?([1-9]\d*)\z/ # ASCII only: /i also matches the Kelvin sign
238
+ FORMS = 'a range is (first_row, first_col, last_row, last_col), "A1:D10" or (rows, cols)'
239
+
240
+ module_function
241
+
242
+ # Splits a range off the front of args and checks how many args follow.
243
+ # Returns [[first_row, first_col, last_row, last_col], the args after it].
244
+ def split(args, following = 0..0)
245
+ range, rest = parse(args)
246
+ unless following.cover?(rest.size)
247
+ expected = following.minmax.uniq.join("..")
248
+ raise ArgumentError, "wrong number of arguments after the cell range (given #{rest.size}, " \
249
+ "expected #{expected}); #{FORMS}"
250
+ end
251
+
252
+ [range, rest]
253
+ end
254
+
255
+ def parse(args)
256
+ case args
257
+ in [Numeric, Numeric, Numeric, Numeric, *rest] then [args.first(4), rest]
258
+ in [String => ref, *rest] then [excel(ref), rest]
259
+ in [Integer | Range => rows, Integer | Range => cols, *rest]
260
+ [rows_and_cols(rows, cols), rest]
261
+ else
262
+ raise ArgumentError, "expected a cell range, got #{args.inspect}; #{FORMS}"
263
+ end
264
+ end
265
+
266
+ def rows_and_cols(rows, cols)
267
+ first_row, last_row = bounds(rows)
268
+ first_col, last_col = bounds(cols)
269
+ [first_row, first_col, last_row, last_col]
270
+ end
271
+
272
+ # "A1:D10", "$A$1:$D$10" or a single "B2".
273
+ def excel(ref)
274
+ cells = cell_matches(ref)
275
+ rows = cells.map { |m| m[2].to_i - 1 }
276
+ cols = cells.map { |m| column(m[1]) }
277
+ [rows.min, cols.min, rows.max, cols.max]
278
+ end
279
+
280
+ def cell_matches(ref)
281
+ cells = ref.split(":", -1).map { |cell| REF.match(cell) }
282
+ return cells if (1..2).cover?(cells.size) && cells.all?
283
+
284
+ raise ArgumentError, "invalid cell range #{ref.inspect}: use e.g. \"A1:D10\" or \"B2\""
285
+ end
286
+
287
+ # "A" => 0, "AA" => 26.
288
+ def column(letters)
289
+ letters.upcase.each_char.reduce(0) { |n, c| (n * 26) + c.ord - 64 } - 1
290
+ end
291
+
292
+ # [first, last] of an index or a Range.
293
+ def bounds(indexes)
294
+ return [indexes, indexes] if indexes.is_a?(Integer)
295
+ unless indexes.is_a?(Range) && indexes.begin.is_a?(Integer) && indexes.end.is_a?(Integer)
296
+ raise ArgumentError, "expected an Integer or a Range of Integers, got #{indexes.inspect}"
297
+ end
195
298
 
196
- private
299
+ first, last = indexes.minmax
300
+ raise ArgumentError, "empty range #{indexes.inspect}" unless first
197
301
 
198
- def column_bounds(columns)
199
- columns.is_a?(Integer) ? [columns, columns] : columns.minmax
302
+ [first, last]
200
303
  end
201
304
  end
305
+ private_constant :CellRange
202
306
 
203
307
  # Cell style, e.g. Format.new(bold: true). Pass to Worksheet#write.
204
308
  class Format
data/sig/fast_xlsx.rbs CHANGED
@@ -36,7 +36,8 @@ module FastXlsx
36
36
  ?company: String, ?category: String, ?keywords: String,
37
37
  ?comments: String, ?status: String) -> self
38
38
  def to_xlsx: () -> String
39
- def save: (String path) -> void
39
+ def define_name: (String name, String formula) -> self
40
+ def save: (String | _ToPath path) -> void
40
41
  end
41
42
 
42
43
  class Format
@@ -46,7 +47,7 @@ module FastXlsx
46
47
  type underline = bool | :single | :double | :single_accounting | :double_accounting
47
48
 
48
49
  def self.new: (?bold: bool, ?italic: bool, ?underline: underline, ?strikeout: bool, ?text_wrap: bool,
49
- ?shrink: bool, ?num_format: String, ?font_size: Numeric, ?font_name: String,
50
+ ?shrink: bool, ?locked: bool, ?hidden: bool, ?num_format: String, ?font_size: Numeric, ?font_name: String,
50
51
  ?font_color: color, ?bg_color: color, ?font_script: :superscript | :subscript,
51
52
  ?align: :left | :center | :right, ?valign: :top | :center | :bottom,
52
53
  ?rotation: Integer, ?indent: Integer,
@@ -62,7 +63,14 @@ module FastXlsx
62
63
  def column_width: (Integer | Range[Integer] columns, Numeric width) -> self
63
64
  def column_format: (Integer | Range[Integer] columns, Format format) -> self
64
65
  def autofit: () -> self
66
+ # A cell range: four 0-based numbers, an Excel reference ("A1:D10"), or
67
+ # rows and columns as Integers or Ranges. Methods with options take it as
68
+ # *cell_range so the keywords can follow.
69
+ type indexes = Integer | Range[Integer]
70
+ type cell_range = Integer | String | Range[Integer]
65
71
  def autofilter: (Integer first_row, Integer first_col, Integer last_row, Integer last_col) -> self
72
+ | (String range) -> self
73
+ | (indexes rows, indexes cols) -> self
66
74
  def freeze_panes: (Integer row, Integer col) -> self
67
75
  def row_height: (Integer row, Numeric height) -> self
68
76
  def page_breaks: (Array[Integer] rows) -> self
@@ -71,12 +79,20 @@ module FastXlsx
71
79
  def page_footer: (String text, ?margin: Numeric?) -> self
72
80
  def margins: (?left: Numeric?, ?right: Numeric?, ?top: Numeric?, ?bottom: Numeric?,
73
81
  ?header: Numeric?, ?footer: Numeric?) -> self
82
+ type protect_action = :format_cells | :format_columns | :format_rows | :insert_columns | :insert_rows
83
+ | :insert_links | :delete_columns | :delete_rows | :sort | :use_autofilter
84
+ | :use_pivot_tables | :edit_scenarios | :edit_objects
85
+ def group_rows: (Integer | Range[Integer] rows, ?collapsed: bool) -> self
86
+ def group_columns: (Integer | Range[Integer] columns, ?collapsed: bool) -> self
87
+ def protect: (?password: String?, ?allow: protect_action | Array[protect_action] | nil) -> self
74
88
  def merge_range: (Integer first_row, Integer first_col, Integer last_row, Integer last_col, cell value, ?Format? format) -> self
75
- def conditional_format: (Integer first_row, Integer first_col, Integer last_row, Integer last_col,
89
+ | (String range, cell value, ?Format? format) -> self
90
+ | (indexes rows, indexes cols, cell value, ?Format? format) -> self
91
+ def conditional_format: (*cell_range range,
76
92
  type: :cell | :text | :formula | :data_bar | :color_scale,
77
93
  ?criteria: Symbol, ?value: Numeric | String | Array[Numeric | String],
78
94
  ?format: Format, ?colors: 2 | 3) -> self
79
- def data_validation: (Integer first_row, Integer first_col, Integer last_row, Integer last_col,
95
+ def data_validation: (*cell_range range,
80
96
  type: :list | :whole_number | :decimal | :text_length,
81
97
  ?criteria: Symbol, ?value: Numeric | String | Array[Numeric | String],
82
98
  ?input_title: String, ?input_message: String,
@@ -91,7 +107,7 @@ module FastXlsx
91
107
  ?width: Integer, ?height: Integer) -> self
92
108
  type table_column = String | { header: String, ?total: Symbol, ?total_label: String, ?format: Format }
93
109
 
94
- def add_table: (Integer first_row, Integer first_col, Integer last_row, Integer last_col,
110
+ def add_table: (*cell_range range,
95
111
  ?columns: Array[table_column], ?style: Symbol, ?name: String,
96
112
  ?total_row: bool, ?banded_rows: bool, ?autofilter: bool) -> self
97
113
  def name: () -> String
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast_xlsx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Zac
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-30 00:00:00.000000000 Z
11
+ date: 2026-10-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Writes Excel .xlsx files from Ruby through a native Rust extension (rust_xlsxwriter
14
14
  + magnus).