fast_xlsx 0.4.0-aarch64-linux → 0.6.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: e7438a6f69df41153d85c357819655b1f6a22ee1dc73190fc32707b7e12b7a10
4
- data.tar.gz: 5dffe1b70f6486c0bdb5d087b272e19c029f46f2b11c0fcf18babff2911f8557
3
+ metadata.gz: 86c6c3af8bf6612a6dd0ec0024c601fc4eaa2d2837cb3ddd2a8f051061a98b0d
4
+ data.tar.gz: d5ef7d7f70968e0035cdf0082feffc299c00cb461fbc07fe458d93f279811c70
5
5
  SHA512:
6
- metadata.gz: 054da301b24d98a3b6e448e13f0c617fa27a4932b5f1d055b62b720b395fbeb2f98eba1ebb5fcb0e12b0a05bcbe1260dfdd042beb2567dda7c810c3f02b29fc6
7
- data.tar.gz: 3c88b39a8a3f12aeb2b6012b74e175e5376708b70f57e259b753b2a1b005dd1885dd6150291012d2fd0475493366d1da12bf31391dd57f39204551fd078157c0
6
+ metadata.gz: 2136a53ec157e62f1222bf99172efeb27cbbc78730fe5206a8ca531e630868ce6530fb6a35137fc12671629e781f420e89bbc47723eded6128d5184c99a148f9
7
+ data.tar.gz: f81b77ebdd8430e22d92b10477f5b99ae48da3e07ed26d40959b5e2b0b35546cf0ff1fc5aadfb0bc713a064fd39eb31af6e5319c6247b70458cf8435c1bbbcb4
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+ --markup markdown
2
+ --no-private
3
+ --hide-api private
4
+ lib/**/*.rb
5
+ -
6
+ README.md
7
+ CHANGELOG.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2026-10-02
4
+
5
+ ### Added
6
+
7
+ - `Worksheet#selection`, `#top_left_cell` (what is selected and scrolled to when the file opens), `#ignore_error` (turns off Excel's green-triangle warnings in a range) and `#unprotect_range` (a range users can edit on a protected sheet).
8
+ - `Worksheet#hide_rows`, `#hide_columns`, `#row_format` (default format for a row's cells) and `#default_row_height` (call it before `row_format`, `hide_rows` and `group_rows`, which copy the default into their rows; it raises otherwise).
9
+
10
+ ### Fixed
11
+
12
+ - `nil` written with a format (e.g. a border, or a row format in `append`) writes a blank cell with that format; the format was dropped.
13
+
14
+ ## [0.5.0] - 2026-10-01
15
+
16
+ ### Added
17
+
18
+ - API documentation: every public method has YARD docs (shown on rubydoc.info).
19
+
20
+ ### Changed
21
+
22
+ - Errors follow one rule (see the README): a row or column outside the sheet raises `RangeError` (row 1,048,576, column 16,384 or a row of more than 16,384 cells raised `FastXlsx::Error` or `ArgumentError`); a reversed range or single-cell merge raises `ArgumentError` (was `FastXlsx::Error`); a header or footer over 255 characters raises `FastXlsx::Error` like other text over Excel's limits (was `ArgumentError`); a negative or NaN image size, font size or margin raises `ArgumentError` (was written into the file or ignored); `zoom` converts numbers with `to_int` like other arguments (`zoom(150.9)` raised `TypeError`); `column_width` outside 0..255 and `row_height` outside 0..409 raise `ArgumentError` (a negative one hid the column or row).
23
+ - Date and time cells written with a format that has no `num_format` (e.g. `append(row, format: bold)`) get the default date format added, instead of showing as serial numbers. This applies to a cell's own format, a table column's and `column_format`'s; a format with a `num_format` is used as is.
24
+
3
25
  ## [0.4.0] - 2026-10-01
4
26
 
5
27
  ### Changed
data/README.md CHANGED
@@ -82,7 +82,7 @@ ws.write(1, 2, Date.today, date) # format one cell
82
82
 
83
83
  Per-side borders override `border`. Unknown options and invalid values raise `ArgumentError`.
84
84
 
85
- Dates and times written without a format get `yyyy-mm-dd` or `yyyy-mm-dd hh:mm:ss`; a table column's format or `column_format` wins over that. A format you pass replaces it, so give date cells one with a `num_format` (e.g. `Format.new(bold: true, num_format: "yyyy-mm-dd")`), or Excel shows the serial number.
85
+ 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.
86
86
 
87
87
  ### Columns and filters
88
88
 
@@ -101,6 +101,9 @@ ws.zoom(150) # 10..400 percent
101
101
  ws.tab_color("#C00000")
102
102
  ws.hide_gridlines
103
103
  ws.activate # Excel opens on this sheet (un-hides it if hidden)
104
+ ws.selection("B2:C3") # the cells selected when it opens
105
+ ws.top_left_cell("A50") # and the cell scrolled to the top left
106
+ ws.ignore_error("A2:A1000", :number_stored_as_text) # no green triangles (one kind per range; don't overlap ranges)
104
107
  other.hide # the sheet Excel opens on can't be hidden: activate another one first
105
108
 
106
109
  ws.page_setup(landscape: true, paper: :a4, # or :letter, :legal, :tabloid, :a3, :a5, Excel's paper number, 0 = printer default
@@ -130,6 +133,9 @@ Methods that take one cell (`write`, `write_comment`, `insert_image`, `insert_ch
130
133
  ```ruby
131
134
  ws.freeze_panes(1, 0) # keep the first row visible while scrolling
132
135
  ws.row_height(0, 30) # row 1, height in points
136
+ ws.default_row_height(18) # every other row; call it before row_format, hide_rows, group_rows
137
+ ws.row_format(0, header) # default for cells in row 1 written without a format
138
+ ws.hide_rows(10..20) # also hide_columns(3), an index or a Range
133
139
  ws.merge_range(0, 0, 0, 3, "Q3 report", title) # merge A1:D1; the value can be any cell type
134
140
  ws.page_breaks([50, 100]) # print a new page before rows 51 and 101
135
141
  ws.vertical_page_breaks([8]) # and before column I
@@ -164,6 +170,7 @@ input = FastXlsx::Format.new(locked: false)
164
170
  ws.write(1, 1, 0, input) # B2 stays editable
165
171
  ws.protect # lock everything else
166
172
  ws.protect(password: "secret", allow: %i[sort use_autofilter]) # or with a password and allowed actions
173
+ ws.unprotect_range("B2:D10", name: "Inputs") # a range users can still edit (optional password:)
167
174
  ```
168
175
 
169
176
  `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.
@@ -258,12 +265,21 @@ Values are mapped by type:
258
265
  | `FastXlsx::URL.new("https://…")`, `URL.new(url, text: "Title")` | hyperlink (optionally showing other text) |
259
266
  | `FastXlsx::RichString.new(["Total: ", bold], "1,234")` | text with a format per segment |
260
267
  | `true` / `false` | boolean |
261
- | `nil` | empty cell |
268
+ | `nil` | empty cell (a blank cell with the format, when one is given) |
262
269
  | anything else | `to_s` as string |
263
270
 
264
271
  `<<` and `concat` append after the last row written to that worksheet. In `:constant` / `:low` memory mode rows are written to disk as you go, so fill each worksheet top to bottom.
265
272
 
266
- Errors from the writer (invalid sheet names, writes to rows already on disk, …) raise `FastXlsx::Error`.
273
+ ### Errors
274
+
275
+ | Error | Raised for |
276
+ |---|---|
277
+ | `TypeError` | an argument of the wrong type, e.g. a String where a row number goes |
278
+ | `RangeError` | a row or column outside the sheet (1,048,576 × 16,384), or a negative count (an infinite Float raises `FloatDomainError`, a `RangeError`) |
279
+ | `ArgumentError` | the right type but a value that isn't allowed: an unknown option or symbol, an invalid color or cell reference, a reversed range or single-cell merge, a size or margin that is negative, NaN or beyond Excel's limits |
280
+ | `FastXlsx::Error` | what the workbook can't do: duplicate or invalid names, writing to rows already on disk, overlapping merges, hiding the sheet Excel opens on, text over Excel's limits (cells, URLs, headers and footers), unsupported images |
281
+
282
+ Numbers are converted like Ruby's own methods do (`to_int`), so `1.9` as a row is row `1`.
267
283
 
268
284
  ## Performance
269
285
 
@@ -323,7 +339,12 @@ BUNDLE_GEMFILE=bench/Gemfile /usr/bin/time -l bundle exec ruby bench/memory.rb f
323
339
  bundle add fast_xlsx
324
340
  ```
325
341
 
326
- Precompiled gems are built for common platforms; other platforms need a Rust toolchain to install. Requires CRuby 3.3+; JRuby and TruffleRuby are not supported because this is a native extension.
342
+ ### Support and versioning
343
+
344
+ - **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).
345
+ - **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.
346
+ - **Versions:** from 1.0, [Semantic Versioning](https://semver.org): no breaking API change before 2.0. Until then a minor release (0.x.0) can change behaviour; the [changelog](CHANGELOG.md) lists every change under "Changed".
347
+ - **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.
327
348
 
328
349
  ## Development
329
350
 
data/bench/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- fast_xlsx (0.4.0)
4
+ fast_xlsx (0.6.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.4.0)
80
+ fast_xlsx (0.6.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
Binary file
Binary file
Binary file
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FastXlsx
4
- VERSION = "0.4.0"
4
+ # The gem version.
5
+ VERSION = "0.6.0"
5
6
  end
data/lib/fast_xlsx.rb CHANGED
@@ -3,21 +3,48 @@
3
3
  require_relative "fast_xlsx/version"
4
4
 
5
5
  # Fast .xlsx writer backed by rust_xlsxwriter.
6
+ #
7
+ # @example
8
+ # wb = FastXlsx::Workbook.new
9
+ # ws = wb.add_worksheet("Report")
10
+ # ws << ["id", "name", "created_at"]
11
+ # ws.concat(records.map { |r| [r.id, r.name, r.created_at] })
12
+ # wb.save("report.xlsx")
13
+ #
14
+ # Rows and columns are 0-based. Errors: TypeError for an argument of the wrong
15
+ # type, RangeError for a row or column outside the sheet, ArgumentError for a
16
+ # value that isn't allowed, {FastXlsx::Error} for what the workbook can't do.
6
17
  module FastXlsx
18
+ # Raised for what the workbook can't do: duplicate or invalid names, writing
19
+ # to rows already on disk, overlapping merges, text over Excel's limits, ...
7
20
  class Error < StandardError; end
8
21
 
9
- # Cell value written as an Excel formula, e.g. Formula.new("SUM(A1:A9)").
22
+ # A cell value written as an Excel formula.
23
+ #
24
+ # @!attribute [r] expression
25
+ # @return [String] the formula, without the leading "="
26
+ # @example
27
+ # ws << [1, 2, FastXlsx::Formula.new("SUM(A1:B1)")]
10
28
  Formula = Data.define(:expression) do
11
29
  def initialize(expression:)
12
30
  super(expression: expression.to_s)
13
31
  end
14
32
  end
15
33
 
16
- # Cell value written as a hyperlink, e.g. URL.new("https://example.com").
17
- # text: shown in the cell instead of the URL itself.
34
+ # A cell value written as a hyperlink.
35
+ #
36
+ # @!attribute [r] url
37
+ # @return [String] http(s)://, mailto:, file:// or internal:Sheet2!A1
38
+ # @!attribute [r] text
39
+ # @return [String, nil] shown in the cell instead of the URL
40
+ # @example
41
+ # FastXlsx::URL.new("https://example.com")
42
+ # FastXlsx::URL.new("https://example.com/report/42", text: "Q3 report")
18
43
  # Subclassed (not a Data.define block) so .new can call super, which lets it
19
44
  # accept URL.new(url, text: ...) as well as the usual Data forms.
20
45
  class URL < Data.define(:url, :text) # rubocop:disable Style/DataInheritance
46
+ # @overload new(url, text = nil)
47
+ # @overload new(url:, text: nil)
21
48
  def self.new(*args, **kwargs)
22
49
  raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2
23
50
 
@@ -32,11 +59,18 @@ module FastXlsx
32
59
  end
33
60
  end
34
61
 
35
- # Text with a format per segment, e.g. RichString.new(["Total: ", bold], "1,234").
36
- # Each segment is a String (default font) or [String, Format].
62
+ # Text with a format per segment.
63
+ #
64
+ # @example
65
+ # bold = FastXlsx::Format.new(bold: true)
66
+ # ws << [FastXlsx::RichString.new(["Total: ", bold], "1,234")]
37
67
  class RichString
68
+ # @return [Array<Array(String, Format)>] [text, format or nil] per segment
38
69
  attr_reader :segments
39
70
 
71
+ # @param parts [Array<String, Array(String, Format)>] each a String (default
72
+ # font) or [String, Format]
73
+ # @raise [ArgumentError] when no segment is given
40
74
  def initialize(*parts)
41
75
  raise ArgumentError, "RichString needs at least one segment" if parts.empty?
42
76
 
@@ -53,15 +87,31 @@ module FastXlsx
53
87
  require "fast_xlsx/fast_xlsx"
54
88
  end
55
89
 
56
- # Owns the worksheets; serialize with #to_xlsx or #save.
90
+ # Owns the worksheets; serialize with {#to_xlsx} or {#save}.
91
+ #
92
+ # @!method to_xlsx
93
+ # The workbook as an .xlsx file. Runs without Ruby's global lock, so
94
+ # other threads keep running meanwhile.
95
+ # @return [String] binary
96
+ # @raise [FastXlsx::Error] e.g. duplicate defined names
97
+ #
98
+ # @!method define_name(name, formula)
99
+ # A defined name: "Rate" for the whole workbook, "Sheet1!Sales" for one
100
+ # sheet. Duplicates and unknown sheets are reported when saving.
101
+ # @param name [String]
102
+ # @param formula [String] e.g. "=0.96" or "=Sheet1!$A$1:$A$9"
103
+ # @return [self]
104
+ # @raise [FastXlsx::Error] for a name Excel doesn't allow
57
105
  class Workbook
106
+ # Accepted memory: modes.
58
107
  MEMORY_MODES = %i[standard constant low].freeze
59
108
 
60
- # memory: :standard keeps every cell in memory until saving. :constant and
61
- # :low write each finished row to disk, so each worksheet must be filled
62
- # top to bottom. :constant stores strings inline (memory stays flat); :low
63
- # keeps Excel's shared string table (memory grows with the number of
64
- # unique strings, output is standard).
109
+ # @param memory [Symbol] :standard keeps every cell in memory until saving.
110
+ # :constant and :low write each finished row to disk, so each worksheet
111
+ # must be filled top to bottom: :constant stores strings inline (memory
112
+ # stays flat), :low keeps Excel's shared string table (memory grows with
113
+ # the number of unique strings).
114
+ # @raise [ArgumentError] for an unknown mode
65
115
  def self.new(memory: :standard)
66
116
  unless MEMORY_MODES.include?(memory)
67
117
  raise ArgumentError, "unknown memory mode #{memory.inspect} (expected one of #{MEMORY_MODES.join(", ")})"
@@ -70,28 +120,40 @@ module FastXlsx
70
120
  _new(memory == :constant, memory == :low)
71
121
  end
72
122
 
123
+ # @param name [String, nil] nil takes the first free "SheetN"
124
+ # @return [Worksheet]
125
+ # @raise [FastXlsx::Error] for an invalid name or one already used
126
+ # (names ignore case)
73
127
  def add_worksheet(name = nil)
74
128
  _add_worksheet(name).tap { |ws| worksheets << ws }
75
129
  end
76
130
 
77
131
  # The same Worksheet objects add_worksheet returned, so their append
78
132
  # position is shared.
133
+ # @return [Array<Worksheet>]
79
134
  def worksheets
80
135
  @worksheets ||= []
81
136
  end
82
137
 
138
+ # @param name [String]
139
+ # @return [Worksheet, nil]
83
140
  def worksheet(name)
84
141
  worksheets.find { |ws| ws.name == name }
85
142
  end
86
143
 
87
- # path: a String, Pathname or anything responding to #to_path.
144
+ # Writes the .xlsx file. Runs without Ruby's global lock, like {#to_xlsx}.
145
+ # @param path [String, Pathname, #to_path]
146
+ # @return [nil]
88
147
  def save(path)
89
148
  _save(File.path(path))
90
149
  end
91
150
 
92
- # Document properties shown in Excel's File > Info: title:, subject:,
93
- # author:, manager:, company:, category:, keywords:, comments:, status:.
94
- # Later calls add to earlier ones.
151
+ # Document properties shown in Excel's File > Info. Later calls add to
152
+ # earlier ones.
153
+ # @param fields [Hash{Symbol => String}] title:, subject:, author:,
154
+ # manager:, company:, category:, keywords:, comments:, status:
155
+ # @return [self]
156
+ # @raise [ArgumentError] for an unknown field
95
157
  def properties(**fields)
96
158
  merged = (@properties || {}).merge(fields)
97
159
  _properties(merged) # validates before anything is remembered
@@ -100,7 +162,81 @@ module FastXlsx
100
162
  end
101
163
  end
102
164
 
103
- # Cell writer for one sheet; create with Workbook#add_worksheet.
165
+ # Cell writer for one sheet; create with {Workbook#add_worksheet}.
166
+ #
167
+ # A cell is (row, col) or a reference like "B2". A range is four 0-based
168
+ # numbers (first_row, first_col, last_row, last_col), a reference like
169
+ # "A1:D10", or rows and columns as Integers or Ranges, e.g. (0..9, 0..3).
170
+ #
171
+ # @!method write(*cell, value, format = nil)
172
+ # Writes one cell.
173
+ # @overload write(row, col, value, format = nil)
174
+ # @overload write(ref, value, format = nil)
175
+ # @param value [Numeric, String, Time, Date, DateTime, true, false, nil,
176
+ # Formula, URL, RichString, #to_s] dates get yyyy-mm-dd (hh:mm:ss) unless
177
+ # the format has a num_format
178
+ # @param format [Format, nil]
179
+ # @return [self]
180
+ #
181
+ # @!method <<(values)
182
+ # Appends a row after the last row written.
183
+ # @param values [Array] cell values, as for {#write}
184
+ # @return [self]
185
+ #
186
+ # @!method concat(rows)
187
+ # Appends several rows.
188
+ # @param rows [Array<Array>]
189
+ # @return [self]
190
+ #
191
+ # @!method next_row
192
+ # @return [Integer] the row {#<<} writes next
193
+ #
194
+ # @!method name
195
+ # @return [String]
196
+ #
197
+ # @!method row_height(row, height)
198
+ # @param height [Numeric] points, 0..409
199
+ # @return [self]
200
+ # @raise [ArgumentError] for a height outside 0..409
201
+ #
202
+ # @!method page_breaks(rows)
203
+ # Starts a printed page before each of these rows.
204
+ # @param rows [Array<Integer>]
205
+ # @return [self]
206
+ #
207
+ # @!method vertical_page_breaks(cols)
208
+ # Starts a printed page before each of these columns.
209
+ # @param cols [Array<Integer>]
210
+ # @return [self]
211
+ #
212
+ # @!method activate
213
+ # Makes Excel open on this sheet (un-hiding it if hidden).
214
+ # @return [self]
215
+ #
216
+ # @!method hide
217
+ # @return [self]
218
+ # @raise [FastXlsx::Error] for the sheet Excel opens on (the first one
219
+ # unless another is activated)
220
+ #
221
+ # @!method zoom(percent)
222
+ # @param percent [Integer, #to_int] 10..400
223
+ # @return [self]
224
+ #
225
+ # @!method tab_color(color)
226
+ # @param color [String, Integer] "#RRGGBB" or 0xRRGGBB
227
+ # @return [self]
228
+ #
229
+ # @!method default_row_height(height)
230
+ # Height of rows not given one with {#row_height}. Call it before
231
+ # {#row_format}, {#hide_rows} and {#group_rows}: rows given those keep
232
+ # the earlier default.
233
+ # @param height [Numeric] points, above 0 and up to 409
234
+ # @return [self]
235
+ # @raise [FastXlsx::Error] when called after those
236
+ #
237
+ # @!method hide_gridlines
238
+ # Hides the gridlines on screen (see {#page_setup} for printing).
239
+ # @return [self]
104
240
  class Worksheet
105
241
  # write(row, col, value, format = nil) or write("B2", value, format = nil)
106
242
  # is native: the (row, col) form runs once per cell, so it skips a Ruby
@@ -112,11 +248,18 @@ module FastXlsx
112
248
  end
113
249
  private :_write_ref
114
250
 
251
+ # Appends a row after the last row written.
252
+ # @param values [Array] cell values, as for {#write}
253
+ # @param format [Format, Array<Format, nil>, nil] one for every cell, or
254
+ # one per cell
255
+ # @return [self]
115
256
  def append(values, format: nil)
116
257
  _append(values, format)
117
258
  end
118
259
 
119
- # columns: a 0-based column index or a Range of them. width is in characters.
260
+ # @param columns [Integer, Range<Integer>]
261
+ # @param width [Numeric] characters, 0..255
262
+ # @return [self]
120
263
  def column_width(columns, width)
121
264
  range = CellRange.bounds(columns)
122
265
  _column_width(*range, width)
@@ -126,89 +269,131 @@ module FastXlsx
126
269
  self
127
270
  end
128
271
 
129
- # Sizes columns to the data written so far. Widths set with
130
- # column_width are kept.
272
+ # Sizes columns to the data written so far (in :constant / :low mode, the
273
+ # rows still in memory). Widths set with {#column_width} are kept.
274
+ # @return [self]
131
275
  def autofit
132
276
  _autofit
133
277
  @fixed_widths&.each { |bounds, width| _column_width(*bounds, width) }
134
278
  self
135
279
  end
136
280
 
137
- # The methods below take a cell range in any of these styles:
138
- # (first_row, first_col, last_row, last_col) four 0-based numbers
139
- # ("A1:D10") or ("B2") an Excel reference
140
- # (rows, cols) Integers or Ranges, e.g. (0..9, 0..3)
141
-
142
281
  # Filter buttons on the range's first row.
282
+ # @param range a cell range (see {Worksheet})
283
+ # @return [self]
143
284
  def autofilter(*range)
144
285
  _autofilter(*CellRange.split(range).first)
145
286
  end
146
287
 
147
288
  # Merges the range and writes value (any cell type) into its first cell.
289
+ # @overload merge_range(*range, value, format = nil)
290
+ # @return [self]
291
+ # @raise [FastXlsx::Error] when it overlaps an earlier merge
148
292
  def merge_range(*args)
149
293
  range, (value, format) = CellRange.split(args, 1..2)
150
294
  _merge_range(*range, value, format)
151
295
  end
152
296
 
153
- # Highlights cells in the range by rule. type: :cell, :text, :formula,
154
- # :data_bar or :color_scale; see the README for each type's options.
297
+ # Highlights cells in the range by rule; see the README for each type's
298
+ # options.
299
+ # @param range a cell range (see {Worksheet})
300
+ # @param type [Symbol] :cell, :text, :formula, :data_bar or :color_scale
301
+ # @option options [Symbol] :criteria e.g. :>, :between, :contains
302
+ # @option options [Numeric, String, Array] :value
303
+ # @option options [Format] :format
304
+ # @option options [Integer] :colors 2 or 3 (:color_scale)
305
+ # @return [self]
155
306
  def conditional_format(*range, type:, **)
156
307
  _conditional_format(*CellRange.split(range).first, { type: type, ** })
157
308
  end
158
309
 
159
- # Restricts what can be entered in the range. type: :list, :whole_number,
160
- # :decimal or :text_length; see the README for the options.
310
+ # Restricts what can be entered in the range; see the README.
311
+ # @param range a cell range (see {Worksheet})
312
+ # @param type [Symbol] :list, :whole_number, :decimal or :text_length
313
+ # @option options [Symbol] :criteria e.g. :>, :between
314
+ # @option options [Numeric, String, Array] :value
315
+ # @option options [String] :input_title, :input_message, :error_title, :error_message
316
+ # @return [self]
161
317
  def data_validation(*range, type:, **)
162
318
  _data_validation(*CellRange.split(range).first, { type: type, ** })
163
319
  end
164
320
 
165
- # Adds a comment (Excel "note") to a cell.
321
+ # Adds a comment (an Excel "note") to a cell.
322
+ # @overload write_comment(*cell, text, author: nil)
323
+ # @return [self]
166
324
  def write_comment(*args, author: nil)
167
325
  row, col, (text, *) = CellRange.cell(args, 1..1)
168
326
  _write_comment(row, col, text, author)
169
327
  end
170
328
 
171
- # Inserts a PNG, JPEG, GIF or BMP image with its top-left corner in the
172
- # cell. source is a file path or an IO (anything responding to #read).
173
- # Options: scale: or width:/height: (pixels), x_offset:, y_offset: (pixels), alt_text:.
329
+ # Inserts a PNG, JPEG, GIF or BMP image with its top-left corner in the cell.
330
+ # @overload insert_image(*cell, source, **options)
331
+ # @param source [String, #read] a file path or an IO
332
+ # @option options [Numeric] :scale
333
+ # @option options [Numeric] :width, :height pixels (the other keeps the ratio)
334
+ # @option options [Integer] :x_offset, :y_offset pixels
335
+ # @option options [String] :alt_text
336
+ # @return [self]
337
+ # @raise [FastXlsx::Error] for data that isn't a supported image
174
338
  def insert_image(*args, **)
175
339
  row, col, (source, *) = CellRange.cell(args, 1..1)
176
340
  bytes = source.respond_to?(:read) ? source.read : File.binread(source)
177
341
  _insert_image(row, col, bytes, { ** })
178
342
  end
179
343
 
180
- # Inserts a chart with its top-left corner in the cell. series is an Array
181
- # of { values:, categories:, name: } with Excel ranges such as
182
- # "Sheet1!$B$2:$B$13". Options: title:, x_axis:, y_axis:, width:, height:.
344
+ # Inserts a chart with its top-left corner in the cell.
345
+ # @param cell (row, col) or "B2"
346
+ # @param type [Symbol] :area, :bar, :column, :line, :pie, :doughnut,
347
+ # :radar, :scatter, and the stacked variants
348
+ # @param series [Array<Hash>] { values:, categories:, name: } with ranges
349
+ # such as "Sheet1!$B$2:$B$13"
350
+ # @option options [String] :title, :x_axis, :y_axis
351
+ # @option options [Integer] :width, :height pixels
352
+ # @return [self]
183
353
  def insert_chart(*cell, type:, series:, **)
184
354
  row, col, = CellRange.cell(cell)
185
355
  _insert_chart(row, col, { type: type, series: series, ** })
186
356
  end
187
357
 
188
358
  # Turns the range (header row included, total row too when total_row: true)
189
- # into an Excel table. columns: header Strings or { header:, total:,
190
- # total_label:, format: }; other options: style:, name:, total_row:,
191
- # banded_rows:, autofilter:.
359
+ # into an Excel table. Rows appended later under the header fill it.
360
+ # @param range a cell range (see {Worksheet})
361
+ # @option options [Array<String, Hash>] :columns header Strings or
362
+ # { header:, total:, total_label:, format: }
363
+ # @option options [Symbol] :style e.g. :medium2
364
+ # @option options [String] :name
365
+ # @option options [Boolean] :total_row, :banded_rows, :autofilter
366
+ # @return [self]
192
367
  def add_table(*range, **)
193
368
  _add_table(*CellRange.split(range).first, { ** })
194
369
  end
195
370
 
196
- # Printed page header/footer using Excel codes such as "&CPage &P of &N".
197
- # margin: is in inches.
371
+ # Printed page header, using Excel codes such as "&CPage &P of &N".
372
+ # @param margin [Numeric, nil] inches
373
+ # @return [self]
198
374
  def page_header(text, margin: nil)
199
375
  _page_header(text)
200
376
  margin ? margins(header: margin) : self
201
377
  end
202
378
 
379
+ # Printed page footer; see {#page_header}.
380
+ # @return [self]
203
381
  def page_footer(text, margin: nil)
204
382
  _page_footer(text)
205
383
  margin ? margins(footer: margin) : self
206
384
  end
207
385
 
208
- # Printing: landscape:, paper: (:letter, :legal, :tabloid, :a3, :a4, :a5 or
209
- # Excel's paper number), fit_width:/fit_height: (pages; 0 or left out =
210
- # as many as needed), repeat_rows:/repeat_columns: (an index or Range,
211
- # printed on every page), print_area: (any cell range), gridlines:.
386
+ # Printing options.
387
+ # @option options [Boolean] :landscape
388
+ # @option options [Symbol, Integer] :paper :letter, :legal, :tabloid, :a3,
389
+ # :a4, :a5 or Excel's paper number (0: the printer's default)
390
+ # @option options [Integer] :fit_width, :fit_height pages (0 or left out:
391
+ # as many as needed)
392
+ # @param repeat_rows [Integer, Range, nil] printed on every page
393
+ # @param repeat_columns [Integer, Range, nil] printed on every page
394
+ # @param print_area a cell range: "A1:D100" or [first_row, first_col, last_row, last_col]
395
+ # @option options [Boolean] :gridlines print the gridlines
396
+ # @return [self]
212
397
  def page_setup(repeat_rows: nil, repeat_columns: nil, print_area: nil, **options)
213
398
  options[:repeat_rows] = CellRange.bounds(repeat_rows) if repeat_rows
214
399
  options[:repeat_columns] = CellRange.bounds(repeat_columns) if repeat_columns
@@ -218,39 +403,121 @@ module FastXlsx
218
403
  end
219
404
 
220
405
  # Print margins in inches; margins not given keep their current value.
406
+ # @return [self]
407
+ # @raise [ArgumentError] for a negative or NaN margin
221
408
  def margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil)
222
- _margins(*[left, right, top, bottom, header, footer].map { |m| m || -1.0 })
409
+ _margins(left, right, top, bottom, header, footer)
223
410
  self
224
411
  end
225
412
 
226
- # Outline group with an expand/collapse button. rows: a 0-based row index
227
- # or a Range; grouping rows already grouped nests them (up to 7 levels).
228
- # collapsed: true hides them until expanded.
413
+ # Outline group with an expand/collapse button. Grouping rows already
414
+ # grouped nests them (up to 7 levels).
415
+ # @param rows [Integer, Range<Integer>]
416
+ # @param collapsed [Boolean] hidden until expanded
417
+ # @return [self]
418
+ # @raise [FastXlsx::Error] in :constant / :low memory mode, which can't
419
+ # write outline levels for rows
229
420
  def group_rows(rows, collapsed: false)
230
421
  _group_rows(*CellRange.bounds(rows), collapsed)
231
422
  end
232
423
 
424
+ # Outline group of columns; see {#group_rows}. Works in every memory mode.
425
+ # @param columns [Integer, Range<Integer>]
426
+ # @return [self]
233
427
  def group_columns(columns, collapsed: false)
234
428
  _group_columns(*CellRange.bounds(columns), collapsed)
235
429
  end
236
430
 
237
431
  # Keeps the rows above and the columns left of the cell visible while
238
432
  # scrolling: (1, 0) or "A2" freezes the first row.
433
+ # @return [self]
239
434
  def freeze_panes(*cell)
240
435
  _freeze_panes(*CellRange.cell(cell).first(2))
241
436
  self
242
437
  end
243
438
 
244
439
  # Locks the sheet against editing. Cells whose format has locked: false
245
- # stay editable. allow: actions users may still take, any of :format_cells,
246
- # :format_columns, :format_rows, :insert_columns, :insert_rows,
247
- # :insert_links, :delete_columns, :delete_rows, :sort, :use_autofilter,
248
- # :use_pivot_tables, :edit_scenarios, :edit_objects.
440
+ # stay editable. Calling it again replaces the password and actions.
441
+ # @param password [String, nil] stops editing in Excel; it doesn't
442
+ # encrypt the file
443
+ # @param allow [Symbol, Array<Symbol>] actions users may still take:
444
+ # :format_cells, :format_columns, :format_rows, :insert_columns,
445
+ # :insert_rows, :insert_links, :delete_columns, :delete_rows, :sort,
446
+ # :use_autofilter, :use_pivot_tables, :edit_scenarios, :edit_objects
447
+ # @return [self]
249
448
  def protect(password: nil, allow: [])
250
449
  _protect(password, Array(allow))
251
450
  end
252
451
 
452
+ # A range users can still edit on a protected sheet ({#protect}),
453
+ # optionally with its own password. Give each range its own name.
454
+ # @overload unprotect_range(*range, name: nil, password: nil)
455
+ # @param name [String, nil] shown in Excel's "Allow Edit Ranges"
456
+ # @return [self]
457
+ def unprotect_range(*range, name: nil, password: nil)
458
+ _unprotect_range(*CellRange.split(range).first, name, password)
459
+ end
460
+
461
+ # The cells selected when the file opens.
462
+ # @param range a cell range (see {Worksheet})
463
+ # @return [self]
464
+ def selection(*range)
465
+ _selection(*CellRange.split(range).first)
466
+ end
467
+
468
+ # The cell scrolled to the top left when the file opens.
469
+ # @param cell (row, col) or "B2"
470
+ # @return [self]
471
+ def top_left_cell(*cell)
472
+ _top_left_cell(*CellRange.cell(cell).first(2))
473
+ end
474
+
475
+ # Turns off one of Excel's warnings (green triangles) in the range, e.g.
476
+ # for codes stored as text. One kind per range (a rust_xlsxwriter limit);
477
+ # overlapping ranges are accepted, but Excel then uses only one of them.
478
+ # @overload ignore_error(*range, error)
479
+ # @param error [Symbol] :number_stored_as_text, :formula_error,
480
+ # :formula_differs, :formula_refers_to_empty_cells,
481
+ # :formula_omits_cells, :data_validation_error,
482
+ # :unlocked_cells_with_formula, :inconsistent_column_formula
483
+ # @return [self]
484
+ # @raise [FastXlsx::Error] when the range already has one
485
+ def ignore_error(*args)
486
+ range, (error, *) = CellRange.split(args, 1..1)
487
+ _ignore_error(*range, error)
488
+ end
489
+
490
+ # Each row in the range is kept (about 1 KB) until saving.
491
+ # @param rows [Integer, Range<Integer>]
492
+ # @return [self]
493
+ # @raise [FastXlsx::Error] in :constant / :low mode, for rows already on disk
494
+ def hide_rows(rows)
495
+ _hide_rows(*CellRange.bounds(rows))
496
+ end
497
+
498
+ # @param columns [Integer, Range<Integer>]
499
+ # @return [self]
500
+ def hide_columns(columns)
501
+ _hide_columns(*CellRange.bounds(columns))
502
+ end
503
+
504
+ # Default format for cells in these rows that are written without one.
505
+ # Use it or {#column_format} for a cell, not both: where both apply, Excel
506
+ # may combine them or show the row's. Each row is kept (about 1 KB) until
507
+ # saving, so style a whole sheet with {#column_format}.
508
+ # @param rows [Integer, Range<Integer>]
509
+ # @param format [Format]
510
+ # @return [self]
511
+ # @raise [FastXlsx::Error] in :constant / :low mode, for rows already on disk
512
+ def row_format(rows, format)
513
+ _row_format(*CellRange.bounds(rows), format)
514
+ self
515
+ end
516
+
253
517
  # Default format for cells in these columns that are written without one.
518
+ # @param columns [Integer, Range<Integer>]
519
+ # @param format [Format]
520
+ # @return [self]
254
521
  def column_format(columns, format)
255
522
  _column_format(*CellRange.bounds(columns), format)
256
523
  self
@@ -260,6 +527,7 @@ module FastXlsx
260
527
  # Cell ranges in the styles Worksheet methods accept: four 0-based numbers,
261
528
  # an Excel reference ("A1:D10", "B2"), or rows and columns as Integers or
262
529
  # Ranges.
530
+ # @api private
263
531
  module CellRange
264
532
  REF = /\A\$?([A-Za-z]{1,3})\$?([1-9]\d*)\z/ # ASCII only: /i also matches the Kelvin sign
265
533
  FORMS = 'a range is (first_row, first_col, last_row, last_col), "A1:D10" or (rows, cols)'
@@ -279,8 +547,8 @@ module FastXlsx
279
547
  case args
280
548
  in [Numeric, Numeric, Numeric, Numeric, *rest] then [args.first(4), rest]
281
549
  in [String => ref, *rest] then [excel(ref), rest]
282
- in [Integer | Range => rows, Integer | Range => cols, *rest]
283
- [rows_and_cols(rows, cols), rest]
550
+ in [Integer | Range, Integer | Range, *rest] # (no "=> name" here: YARD can't parse it)
551
+ [rows_and_cols(args[0], args[1]), rest]
284
552
  else
285
553
  raise ArgumentError, "expected a cell range, got #{args.inspect}; #{FORMS}"
286
554
  end
@@ -351,8 +619,19 @@ module FastXlsx
351
619
  end
352
620
  private_constant :CellRange
353
621
 
354
- # Cell style, e.g. Format.new(bold: true). Pass to Worksheet#write.
622
+ # A cell style; pass it to {Worksheet#write}, {Worksheet#append} and the
623
+ # other methods that take a format. See the README for every option.
624
+ #
625
+ # @example
626
+ # header = FastXlsx::Format.new(bold: true, bg_color: "#DDEBF7", border_bottom: :thin)
627
+ # money = FastXlsx::Format.new(num_format: "#,##0.00")
355
628
  class Format
629
+ # @param options [Hash] bold:, italic:, underline:, strikeout:,
630
+ # font_script:, font_size:, font_name:, font_color:, bg_color:,
631
+ # num_format:, align:, valign:, text_wrap:, rotation:, indent:, shrink:,
632
+ # border:, border_left:, border_right:, border_top:, border_bottom:,
633
+ # border_color:, locked:, hidden:
634
+ # @raise [ArgumentError] for an unknown option or an invalid value
356
635
  def self.new(**options)
357
636
  # Apply border: first so border_left: etc. override it whatever the order.
358
637
  options = { border: options[:border], **options.except(:border) } if options.key?(:border)
data/sig/fast_xlsx.rbs CHANGED
@@ -62,6 +62,18 @@ module FastXlsx
62
62
  def <<: (Array[cell] row) -> self
63
63
  def concat: (Array[Array[cell]] rows) -> self
64
64
  def column_width: (Integer | Range[Integer] columns, Numeric width) -> self
65
+ def hide_rows: (Integer | Range[Integer] rows) -> self
66
+ def hide_columns: (Integer | Range[Integer] columns) -> self
67
+ def row_format: (Integer | Range[Integer] rows, Format format) -> self
68
+ def default_row_height: (Numeric height) -> self
69
+ def selection: (*cell_range range) -> self
70
+ def top_left_cell: (Integer row, Integer col) -> self
71
+ | (String cell) -> self
72
+ type ignorable_error = :number_stored_as_text | :formula_error | :formula_differs | :formula_refers_to_empty_cells
73
+ | :formula_omits_cells | :data_validation_error | :unlocked_cells_with_formula
74
+ | :inconsistent_column_formula
75
+ def ignore_error: (*cell_range | ignorable_error args) -> self
76
+ def unprotect_range: (*cell_range range, ?name: String?, ?password: String?) -> self
65
77
  def column_format: (Integer | Range[Integer] columns, Format format) -> self
66
78
  def autofit: () -> self
67
79
  # A cell range: four 0-based numbers, an Excel reference ("A1:D10"), or
@@ -81,7 +93,7 @@ module FastXlsx
81
93
  def page_footer: (String text, ?margin: Numeric?) -> self
82
94
  def activate: () -> self
83
95
  def hide: () -> self
84
- def zoom: (Integer percent) -> self
96
+ def zoom: (int percent) -> self
85
97
  def tab_color: (String | Integer color) -> self
86
98
  def hide_gridlines: () -> self
87
99
  def page_setup: (?landscape: bool, ?paper: :letter | :legal | :tabloid | :a3 | :a4 | :a5 | Integer,
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.0
4
+ version: 0.6.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Zac
@@ -18,6 +18,7 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - ".yardopts"
21
22
  - CHANGELOG.md
22
23
  - LICENSE.txt
23
24
  - README.md