fast_xlsx 0.3.0-aarch64-linux → 0.5.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: 11994ead3fe4a960a3a8d0da0b6550b84f835bbf1db7548ca3d9c15ec636a6ce
4
- data.tar.gz: 32a563fd7df516a3347f3aedd93139cf64de65809ba759c99e19936cb68db731
3
+ metadata.gz: d5ce755a7313239e1f8aed891577dd789243053ba8898298449e6f374f7fa1b0
4
+ data.tar.gz: 981326856ac95f0b000f6129608b66f9fd426add3b67693b606442f74e8908d7
5
5
  SHA512:
6
- metadata.gz: 4d5e61fd835ec50eeb81b4e5fce8bd46c472f22fc398324988ed624af9eca3b618948b0e7bf9569ae0038f5799ab17543f107e7b155eac0f51a5786ee420faf2
7
- data.tar.gz: f0bec1819859e488a3abb455606eb18e513fa9dcf0ee05a095d4427ace8c96cdfcdb640f5b182f8d021a2b18cb3b58702e78839fe2d42553d286babbedb4e518
6
+ metadata.gz: 2004ba52b608f7ce2ed3bd8d2d2245c77c7967c2160a33b129841004443fadd9ea49b5895683e2d758f3cece3987cc6b5bcc5a768d55fc1a53a4f08623211418
7
+ data.tar.gz: 60d511eb64ae1935c8bd04b40961c22b74967f8bac929dea16dc2facd01a4eee5c8c5afedd717c30d61fc328b263ccdc79f7c069d53cb4e32109812871642b29
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,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2026-10-01
4
+
5
+ ### Added
6
+
7
+ - API documentation: every public method has YARD docs (shown on rubydoc.info).
8
+
9
+ ### Changed
10
+
11
+ - 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).
12
+ - 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.
13
+
14
+ ## [0.4.0] - 2026-10-01
15
+
16
+ ### Changed
17
+
18
+ - Dates and times written without a format now get `yyyy-mm-dd` (`Date`) or `yyyy-mm-dd hh:mm:ss` (`Time`, `DateTime`) instead of showing as serial numbers. A cell's own format, its table column's and `column_format` still win. Applying the format makes writing date-heavy data about 15% slower, the same as passing a date format yourself.
19
+
3
20
  ## [0.3.0] - 2026-10-01
4
21
 
5
22
  ### Changed
data/README.md CHANGED
@@ -82,6 +82,8 @@ 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 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
+
85
87
  ### Columns and filters
86
88
 
87
89
  ```ruby
@@ -250,8 +252,8 @@ Values are mapped by type:
250
252
  |---|---|
251
253
  | `Integer`, `Float`, any `Numeric` | number |
252
254
  | `String` | string |
253
- | `Time` | number (Excel serial date, local time) |
254
- | `Date`, `DateTime` | number (Excel serial date, own offset) |
255
+ | `Time`, `DateTime` | date and time (`yyyy-mm-dd hh:mm:ss` unless formatted), in its own offset |
256
+ | `Date` | date (`yyyy-mm-dd` unless formatted) |
255
257
  | `FastXlsx::Formula.new("SUM(A1:A9)")` | formula |
256
258
  | `FastXlsx::URL.new("https://…")`, `URL.new(url, text: "Title")` | hyperlink (optionally showing other text) |
257
259
  | `FastXlsx::RichString.new(["Total: ", bold], "1,234")` | text with a format per segment |
@@ -261,7 +263,16 @@ Values are mapped by type:
261
263
 
262
264
  `<<` 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.
263
265
 
264
- Errors from the writer (invalid sheet names, writes to rows already on disk, …) raise `FastXlsx::Error`.
266
+ ### Errors
267
+
268
+ | Error | Raised for |
269
+ |---|---|
270
+ | `TypeError` | an argument of the wrong type, e.g. a String where a row number goes |
271
+ | `RangeError` | a row or column outside the sheet (1,048,576 × 16,384), or a negative count (an infinite Float raises `FloatDomainError`, a `RangeError`) |
272
+ | `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 |
273
+ | `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 |
274
+
275
+ Numbers are converted like Ruby's own methods do (`to_int`), so `1.9` as a row is row `1`.
265
276
 
266
277
  ## Performance
267
278
 
@@ -273,17 +284,17 @@ Apple Silicon, Ruby 4.0.5. Each library uses its own idiomatic row-append API; x
273
284
 
274
285
  | Library | Time | vs fastest | Ruby objects allocated |
275
286
  |---|---:|---:|---:|
276
- | **fast_xlsx** (`memory: :constant`) | **69 ms** | 1.0x | 7 |
277
- | **fast_xlsx** (`memory: :low`) | **78 ms** | 1.1x | 7 |
278
- | **fast_xlsx** | **80 ms** | 1.2x | 10 |
279
- | [xlsxtream](https://github.com/felixbuenemann/xlsxtream) 3.1 | 171 ms | 2.5x | 561,728 |
280
- | [fast_excel](https://github.com/Paxa/fast_excel) 0.5 (constant_memory) | 194 ms | 2.8x | 20,079 |
281
- | [fast_excel](https://github.com/Paxa/fast_excel) 0.5 | 228 ms | 3.3x | 320,076 |
282
- | [write_xlsx](https://github.com/cxn03651/write_xlsx) 1.15 | 583 ms | 8.4x | 1,483,899 |
283
- | [caxlsx](https://github.com/caxlsx/caxlsx) 4.5 | 691 ms | 10.0x | 745,122 |
284
- | [rubyXL](https://github.com/weshatheleopard/rubyXL) 3.4 | 2650 ms | 38.3x | 8,700,448 |
285
-
286
- All outputs are 681–750 KB.
287
+ | **fast_xlsx** (`memory: :constant`) | **77 ms** | 1.0x | 7 |
288
+ | **fast_xlsx** (`memory: :low`) | **86 ms** | 1.1x | 7 |
289
+ | **fast_xlsx** | **89 ms** | 1.1x | 10 |
290
+ | [xlsxtream](https://github.com/felixbuenemann/xlsxtream) 3.1 | 183 ms | 2.4x | 561,728 |
291
+ | [fast_excel](https://github.com/Paxa/fast_excel) 0.5 (constant_memory) | 200 ms | 2.6x | 20,079 |
292
+ | [fast_excel](https://github.com/Paxa/fast_excel) 0.5 | 238 ms | 3.1x | 320,076 |
293
+ | [write_xlsx](https://github.com/cxn03651/write_xlsx) 1.15 | 608 ms | 7.8x | 1,483,899 |
294
+ | [caxlsx](https://github.com/caxlsx/caxlsx) 4.5 | 689 ms | 8.9x | 745,122 |
295
+ | [rubyXL](https://github.com/weshatheleopard/rubyXL) 3.4 | 2755 ms | 35.6x | 8,700,448 |
296
+
297
+ All outputs are 689–750 KB.
287
298
 
288
299
  ### Memory
289
300
 
@@ -321,7 +332,12 @@ BUNDLE_GEMFILE=bench/Gemfile /usr/bin/time -l bundle exec ruby bench/memory.rb f
321
332
  bundle add fast_xlsx
322
333
  ```
323
334
 
324
- 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.
335
+ ### Support and versioning
336
+
337
+ - **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).
338
+ - **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.
339
+ - **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".
340
+ - **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.
325
341
 
326
342
  ## Development
327
343
 
data/bench/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- fast_xlsx (0.3.0)
4
+ fast_xlsx (0.5.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.3.0)
80
+ fast_xlsx (0.5.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.3.0"
4
+ # The gem version.
5
+ VERSION = "0.5.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,73 @@ 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 hide_gridlines
230
+ # Hides the gridlines on screen (see {#page_setup} for printing).
231
+ # @return [self]
104
232
  class Worksheet
105
233
  # write(row, col, value, format = nil) or write("B2", value, format = nil)
106
234
  # is native: the (row, col) form runs once per cell, so it skips a Ruby
@@ -112,11 +240,18 @@ module FastXlsx
112
240
  end
113
241
  private :_write_ref
114
242
 
243
+ # Appends a row after the last row written.
244
+ # @param values [Array] cell values, as for {#write}
245
+ # @param format [Format, Array<Format, nil>, nil] one for every cell, or
246
+ # one per cell
247
+ # @return [self]
115
248
  def append(values, format: nil)
116
249
  _append(values, format)
117
250
  end
118
251
 
119
- # columns: a 0-based column index or a Range of them. width is in characters.
252
+ # @param columns [Integer, Range<Integer>]
253
+ # @param width [Numeric] characters, 0..255
254
+ # @return [self]
120
255
  def column_width(columns, width)
121
256
  range = CellRange.bounds(columns)
122
257
  _column_width(*range, width)
@@ -126,89 +261,131 @@ module FastXlsx
126
261
  self
127
262
  end
128
263
 
129
- # Sizes columns to the data written so far. Widths set with
130
- # column_width are kept.
264
+ # Sizes columns to the data written so far (in :constant / :low mode, the
265
+ # rows still in memory). Widths set with {#column_width} are kept.
266
+ # @return [self]
131
267
  def autofit
132
268
  _autofit
133
269
  @fixed_widths&.each { |bounds, width| _column_width(*bounds, width) }
134
270
  self
135
271
  end
136
272
 
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
273
  # Filter buttons on the range's first row.
274
+ # @param range a cell range (see {Worksheet})
275
+ # @return [self]
143
276
  def autofilter(*range)
144
277
  _autofilter(*CellRange.split(range).first)
145
278
  end
146
279
 
147
280
  # Merges the range and writes value (any cell type) into its first cell.
281
+ # @overload merge_range(*range, value, format = nil)
282
+ # @return [self]
283
+ # @raise [FastXlsx::Error] when it overlaps an earlier merge
148
284
  def merge_range(*args)
149
285
  range, (value, format) = CellRange.split(args, 1..2)
150
286
  _merge_range(*range, value, format)
151
287
  end
152
288
 
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.
289
+ # Highlights cells in the range by rule; see the README for each type's
290
+ # options.
291
+ # @param range a cell range (see {Worksheet})
292
+ # @param type [Symbol] :cell, :text, :formula, :data_bar or :color_scale
293
+ # @option options [Symbol] :criteria e.g. :>, :between, :contains
294
+ # @option options [Numeric, String, Array] :value
295
+ # @option options [Format] :format
296
+ # @option options [Integer] :colors 2 or 3 (:color_scale)
297
+ # @return [self]
155
298
  def conditional_format(*range, type:, **)
156
299
  _conditional_format(*CellRange.split(range).first, { type: type, ** })
157
300
  end
158
301
 
159
- # Restricts what can be entered in the range. type: :list, :whole_number,
160
- # :decimal or :text_length; see the README for the options.
302
+ # Restricts what can be entered in the range; see the README.
303
+ # @param range a cell range (see {Worksheet})
304
+ # @param type [Symbol] :list, :whole_number, :decimal or :text_length
305
+ # @option options [Symbol] :criteria e.g. :>, :between
306
+ # @option options [Numeric, String, Array] :value
307
+ # @option options [String] :input_title, :input_message, :error_title, :error_message
308
+ # @return [self]
161
309
  def data_validation(*range, type:, **)
162
310
  _data_validation(*CellRange.split(range).first, { type: type, ** })
163
311
  end
164
312
 
165
- # Adds a comment (Excel "note") to a cell.
313
+ # Adds a comment (an Excel "note") to a cell.
314
+ # @overload write_comment(*cell, text, author: nil)
315
+ # @return [self]
166
316
  def write_comment(*args, author: nil)
167
317
  row, col, (text, *) = CellRange.cell(args, 1..1)
168
318
  _write_comment(row, col, text, author)
169
319
  end
170
320
 
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:.
321
+ # Inserts a PNG, JPEG, GIF or BMP image with its top-left corner in the cell.
322
+ # @overload insert_image(*cell, source, **options)
323
+ # @param source [String, #read] a file path or an IO
324
+ # @option options [Numeric] :scale
325
+ # @option options [Numeric] :width, :height pixels (the other keeps the ratio)
326
+ # @option options [Integer] :x_offset, :y_offset pixels
327
+ # @option options [String] :alt_text
328
+ # @return [self]
329
+ # @raise [FastXlsx::Error] for data that isn't a supported image
174
330
  def insert_image(*args, **)
175
331
  row, col, (source, *) = CellRange.cell(args, 1..1)
176
332
  bytes = source.respond_to?(:read) ? source.read : File.binread(source)
177
333
  _insert_image(row, col, bytes, { ** })
178
334
  end
179
335
 
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:.
336
+ # Inserts a chart with its top-left corner in the cell.
337
+ # @param cell (row, col) or "B2"
338
+ # @param type [Symbol] :area, :bar, :column, :line, :pie, :doughnut,
339
+ # :radar, :scatter, and the stacked variants
340
+ # @param series [Array<Hash>] { values:, categories:, name: } with ranges
341
+ # such as "Sheet1!$B$2:$B$13"
342
+ # @option options [String] :title, :x_axis, :y_axis
343
+ # @option options [Integer] :width, :height pixels
344
+ # @return [self]
183
345
  def insert_chart(*cell, type:, series:, **)
184
346
  row, col, = CellRange.cell(cell)
185
347
  _insert_chart(row, col, { type: type, series: series, ** })
186
348
  end
187
349
 
188
350
  # 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:.
351
+ # into an Excel table. Rows appended later under the header fill it.
352
+ # @param range a cell range (see {Worksheet})
353
+ # @option options [Array<String, Hash>] :columns header Strings or
354
+ # { header:, total:, total_label:, format: }
355
+ # @option options [Symbol] :style e.g. :medium2
356
+ # @option options [String] :name
357
+ # @option options [Boolean] :total_row, :banded_rows, :autofilter
358
+ # @return [self]
192
359
  def add_table(*range, **)
193
360
  _add_table(*CellRange.split(range).first, { ** })
194
361
  end
195
362
 
196
- # Printed page header/footer using Excel codes such as "&CPage &P of &N".
197
- # margin: is in inches.
363
+ # Printed page header, using Excel codes such as "&CPage &P of &N".
364
+ # @param margin [Numeric, nil] inches
365
+ # @return [self]
198
366
  def page_header(text, margin: nil)
199
367
  _page_header(text)
200
368
  margin ? margins(header: margin) : self
201
369
  end
202
370
 
371
+ # Printed page footer; see {#page_header}.
372
+ # @return [self]
203
373
  def page_footer(text, margin: nil)
204
374
  _page_footer(text)
205
375
  margin ? margins(footer: margin) : self
206
376
  end
207
377
 
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:.
378
+ # Printing options.
379
+ # @option options [Boolean] :landscape
380
+ # @option options [Symbol, Integer] :paper :letter, :legal, :tabloid, :a3,
381
+ # :a4, :a5 or Excel's paper number (0: the printer's default)
382
+ # @option options [Integer] :fit_width, :fit_height pages (0 or left out:
383
+ # as many as needed)
384
+ # @param repeat_rows [Integer, Range, nil] printed on every page
385
+ # @param repeat_columns [Integer, Range, nil] printed on every page
386
+ # @param print_area a cell range: "A1:D100" or [first_row, first_col, last_row, last_col]
387
+ # @option options [Boolean] :gridlines print the gridlines
388
+ # @return [self]
212
389
  def page_setup(repeat_rows: nil, repeat_columns: nil, print_area: nil, **options)
213
390
  options[:repeat_rows] = CellRange.bounds(repeat_rows) if repeat_rows
214
391
  options[:repeat_columns] = CellRange.bounds(repeat_columns) if repeat_columns
@@ -218,39 +395,56 @@ module FastXlsx
218
395
  end
219
396
 
220
397
  # Print margins in inches; margins not given keep their current value.
398
+ # @return [self]
399
+ # @raise [ArgumentError] for a negative or NaN margin
221
400
  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 })
401
+ _margins(left, right, top, bottom, header, footer)
223
402
  self
224
403
  end
225
404
 
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.
405
+ # Outline group with an expand/collapse button. Grouping rows already
406
+ # grouped nests them (up to 7 levels).
407
+ # @param rows [Integer, Range<Integer>]
408
+ # @param collapsed [Boolean] hidden until expanded
409
+ # @return [self]
410
+ # @raise [FastXlsx::Error] in :constant / :low memory mode, which can't
411
+ # write outline levels for rows
229
412
  def group_rows(rows, collapsed: false)
230
413
  _group_rows(*CellRange.bounds(rows), collapsed)
231
414
  end
232
415
 
416
+ # Outline group of columns; see {#group_rows}. Works in every memory mode.
417
+ # @param columns [Integer, Range<Integer>]
418
+ # @return [self]
233
419
  def group_columns(columns, collapsed: false)
234
420
  _group_columns(*CellRange.bounds(columns), collapsed)
235
421
  end
236
422
 
237
423
  # Keeps the rows above and the columns left of the cell visible while
238
424
  # scrolling: (1, 0) or "A2" freezes the first row.
425
+ # @return [self]
239
426
  def freeze_panes(*cell)
240
427
  _freeze_panes(*CellRange.cell(cell).first(2))
241
428
  self
242
429
  end
243
430
 
244
431
  # 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.
432
+ # stay editable. Calling it again replaces the password and actions.
433
+ # @param password [String, nil] stops editing in Excel; it doesn't
434
+ # encrypt the file
435
+ # @param allow [Symbol, Array<Symbol>] actions users may still take:
436
+ # :format_cells, :format_columns, :format_rows, :insert_columns,
437
+ # :insert_rows, :insert_links, :delete_columns, :delete_rows, :sort,
438
+ # :use_autofilter, :use_pivot_tables, :edit_scenarios, :edit_objects
439
+ # @return [self]
249
440
  def protect(password: nil, allow: [])
250
441
  _protect(password, Array(allow))
251
442
  end
252
443
 
253
444
  # Default format for cells in these columns that are written without one.
445
+ # @param columns [Integer, Range<Integer>]
446
+ # @param format [Format]
447
+ # @return [self]
254
448
  def column_format(columns, format)
255
449
  _column_format(*CellRange.bounds(columns), format)
256
450
  self
@@ -260,6 +454,7 @@ module FastXlsx
260
454
  # Cell ranges in the styles Worksheet methods accept: four 0-based numbers,
261
455
  # an Excel reference ("A1:D10", "B2"), or rows and columns as Integers or
262
456
  # Ranges.
457
+ # @api private
263
458
  module CellRange
264
459
  REF = /\A\$?([A-Za-z]{1,3})\$?([1-9]\d*)\z/ # ASCII only: /i also matches the Kelvin sign
265
460
  FORMS = 'a range is (first_row, first_col, last_row, last_col), "A1:D10" or (rows, cols)'
@@ -279,8 +474,8 @@ module FastXlsx
279
474
  case args
280
475
  in [Numeric, Numeric, Numeric, Numeric, *rest] then [args.first(4), rest]
281
476
  in [String => ref, *rest] then [excel(ref), rest]
282
- in [Integer | Range => rows, Integer | Range => cols, *rest]
283
- [rows_and_cols(rows, cols), rest]
477
+ in [Integer | Range, Integer | Range, *rest] # (no "=> name" here: YARD can't parse it)
478
+ [rows_and_cols(args[0], args[1]), rest]
284
479
  else
285
480
  raise ArgumentError, "expected a cell range, got #{args.inspect}; #{FORMS}"
286
481
  end
@@ -351,8 +546,19 @@ module FastXlsx
351
546
  end
352
547
  private_constant :CellRange
353
548
 
354
- # Cell style, e.g. Format.new(bold: true). Pass to Worksheet#write.
549
+ # A cell style; pass it to {Worksheet#write}, {Worksheet#append} and the
550
+ # other methods that take a format. See the README for every option.
551
+ #
552
+ # @example
553
+ # header = FastXlsx::Format.new(bold: true, bg_color: "#DDEBF7", border_bottom: :thin)
554
+ # money = FastXlsx::Format.new(num_format: "#,##0.00")
355
555
  class Format
556
+ # @param options [Hash] bold:, italic:, underline:, strikeout:,
557
+ # font_script:, font_size:, font_name:, font_color:, bg_color:,
558
+ # num_format:, align:, valign:, text_wrap:, rotation:, indent:, shrink:,
559
+ # border:, border_left:, border_right:, border_top:, border_bottom:,
560
+ # border_color:, locked:, hidden:
561
+ # @raise [ArgumentError] for an unknown option or an invalid value
356
562
  def self.new(**options)
357
563
  # Apply border: first so border_left: etc. override it whatever the order.
358
564
  options = { border: options[:border], **options.except(:border) } if options.key?(:border)
data/sig/fast_xlsx.rbs CHANGED
@@ -81,7 +81,7 @@ module FastXlsx
81
81
  def page_footer: (String text, ?margin: Numeric?) -> self
82
82
  def activate: () -> self
83
83
  def hide: () -> self
84
- def zoom: (Integer percent) -> self
84
+ def zoom: (int percent) -> self
85
85
  def tab_color: (String | Integer color) -> self
86
86
  def hide_gridlines: () -> self
87
87
  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.3.0
4
+ version: 0.5.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