xlsxrb 0.1.6 → 0.1.8

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.
data/lib/xlsxrb.rb CHANGED
@@ -24,6 +24,7 @@ require_relative "xlsxrb/ooxml/writer"
24
24
  require_relative "xlsxrb/ooxml/reader"
25
25
  require_relative "xlsxrb/ooxml"
26
26
  require_relative "xlsxrb/elements"
27
+ require_relative "xlsxrb/stream_row"
27
28
  require_relative "xlsxrb/style_builder"
28
29
 
29
30
  # Ruby XLSX read/write library.
@@ -341,11 +342,17 @@ module Xlsxrb
341
342
 
342
343
  # Creates a Formula object for use in row values.
343
344
  #
344
- # @param expression [String] The formula text (e.g. "SUM(A1:A10)").
345
+ # @example Create a basic sum formula
346
+ # formula = Xlsxrb.formula("SUM(A1:A10)")
347
+ #
348
+ # @example Create a formula with precomputed cached value
349
+ # formula = Xlsxrb.formula("A1+B1", cached_value: 42)
350
+ #
351
+ # @param expression [String] The formula text without '=' (e.g. "SUM(A1:A10)").
345
352
  # @param cached_value [Object, nil] Optional cached result. If nil, Excel will calculate on open.
346
353
  # @return [Elements::Formula]
347
354
  # @api public
348
- #: (String expression, ?cached_value: String | Numeric | bool | nil) -> untyped
355
+ #: (String expression, ?cached_value: String | Numeric | bool | nil) -> Elements::Formula
349
356
  def self.formula(expression, cached_value: nil)
350
357
  Elements::Formula.new(
351
358
  expression: expression,
@@ -354,20 +361,46 @@ module Xlsxrb
354
361
  )
355
362
  end
356
363
 
357
- # Reads an XLSX file into an Elements::Workbook.
364
+ # Reads an XLSX file (streaming / lazy-loaded by default) from a file path, IO stream, or binary String.
365
+ #
366
+ # Sheets and rows are streamed lazily with O(1) constant memory. If a block is given,
367
+ # yields each StreamSheet sequentially.
368
+ #
369
+ # Call #load on the returned Workbook or Sheet to convert to an in-memory representation
370
+ # for coordinate random access (e.g. sheet["A1"]).
371
+ #
372
+ # @example Streaming read across sheets and rows (O(1) memory)
373
+ # Xlsxrb.read("large.xlsx") do |sheet|
374
+ # puts "Sheet: #{sheet.name}"
375
+ # sheet.each_row do |row|
376
+ # row.each_cell { |cell| puts "#{cell.ref}: #{cell.value}" }
377
+ # end
378
+ # end
379
+ #
380
+ # @example Lazy workbook access and explicit in-memory loading
381
+ # wb = Xlsxrb.read("data.xlsx")
382
+ # sheet = wb.sheets.first
383
+ # sheet.each_row { |row| ... } # streams with O(1) memory
384
+ # doc_sheet = sheet.load # explicitly load into memory
385
+ # puts doc_sheet["A1"].value # coordinate random access
358
386
  #
359
- # @param source [String, IO] File path or IO object.
360
- # @return [Elements::Workbook] The parsed workbook.
387
+ # @param source [String, IO] File path, binary content string (starting with PK..), or IO object.
388
+ # @yield [sheet] Yields each streaming sheet.
389
+ # @yieldparam sheet [StreamSheet] The streaming worksheet object.
390
+ # @return [Elements::Workbook, void] Returns Elements::Workbook when no block is given.
361
391
  # @api public
362
- #: (untyped source) -> untyped
363
- def self.read(source)
392
+ #: (String | IO source) { (StreamSheet) -> void } -> void
393
+ #: (String | IO source) -> Elements::Workbook
394
+ def self.read(source, &)
395
+ source = StringIO.new(source) if source.is_a?(String) && (source.start_with?("PK\x03\x04") || source.include?("\x00"))
396
+
364
397
  attributes = source.is_a?(String) ? { "filepath" => source } : {}
365
398
  Xlsxrb.in_span("Xlsxrb.read", attributes: attributes) do
366
399
  entries = Ooxml::ZipReader.open(source, &:read_all)
367
400
  shared_strings = Ooxml::SharedStringsParser.parse(entries["xl/sharedStrings.xml"])
368
- styles = Ooxml::StylesParser.parse(entries["xl/styles.xml"])
369
401
  workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"])
370
402
  rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"])
403
+ styles = Ooxml::StylesParser.parse(entries["xl/styles.xml"])
371
404
 
372
405
  sheets = workbook_sheets.map do |sheet_info|
373
406
  target = rels[sheet_info[:r_id]]
@@ -375,21 +408,92 @@ module Xlsxrb
375
408
 
376
409
  sheet_path = target.start_with?("/") ? target.delete_prefix("/") : "xl/#{target}"
377
410
  sheet_xml = entries[sheet_path]
378
- build_worksheet(sheet_info[:name], sheet_xml, shared_strings, styles)
411
+ next nil if sheet_xml.nil? || sheet_xml.empty?
412
+
413
+ StreamSheet.new(
414
+ sheet_info[:name],
415
+ sheet_xml,
416
+ shared_strings,
417
+ styles
418
+ )
379
419
  end.compact
380
420
 
381
- Elements::Workbook.new(sheets: sheets, shared_strings: shared_strings, styles: styles)
421
+ wb = Elements::Workbook.new(sheets: sheets, shared_strings: shared_strings, styles: styles)
422
+
423
+ if block_given?
424
+ sheets.each(&)
425
+ nil
426
+ else
427
+ wb
428
+ end
382
429
  end
383
430
  end
384
431
 
385
- # Writes an Elements::Workbook to an XLSX file.
432
+ # Writes an XLSX file or IO stream (streaming or in-memory), or returns a binary string.
433
+ #
434
+ # @overload write(target, strict_excel_mode: true, &block)
435
+ # Streaming write: yields a StreamWriter context for high-speed, zero-allocation XLSX generation.
436
+ # @param target [String, IO] Destination file path or writable IO object.
437
+ # @param strict_excel_mode [Boolean] Whether to enforce Excel specifications.
438
+ # @yield [stream_writer]
439
+ # @yieldparam stream_writer [Xlsxrb::StreamWriter]
440
+ # @return [void]
441
+ #
442
+ # @overload write(workbook)
443
+ # In-memory write: exports the workbook to an in-memory binary String.
444
+ # @param workbook [Elements::Workbook] The workbook to write.
445
+ # @return [String] Binary data representing the XLSX file.
446
+ #
447
+ # @overload write(target, workbook)
448
+ # In-memory write: writes the workbook to a file path or IO stream.
449
+ # @param target [String, IO] Destination file path or writable IO object.
450
+ # @param workbook [Elements::Workbook] The workbook to write.
451
+ # @return [void]
452
+ #
453
+ # @example Streaming write to file
454
+ # Xlsxrb.write("output.xlsx") do |writer|
455
+ # writer.sheet("Sheet1") { |s| s.row(["Hello", "World"]) }
456
+ # end
457
+ #
458
+ # @example In-memory export to binary string
459
+ # binary_data = Xlsxrb.write(workbook)
460
+ #
461
+ # @example In-memory write to file
462
+ # Xlsxrb.write("output.xlsx", workbook)
386
463
  #
387
- # @param target [String, IO] File path or IO object.
388
- # @param workbook [Elements::Workbook] The workbook to write.
389
- # @return [void]
390
464
  # @api public
391
- #: (untyped target, untyped workbook) -> void
392
- def self.write(target, workbook)
465
+ #: (Elements::Workbook workbook) -> String
466
+ #: (String | IO target, Elements::Workbook workbook) -> void
467
+ #: (String | IO target, ?strict_excel_mode: bool) ?{ (StreamWriter) -> void } -> void
468
+ def self.write(target_or_workbook, workbook_or_nil = nil, strict_excel_mode: true, &block)
469
+ if block_given?
470
+ target = target_or_workbook
471
+ raise Error, "target is required" if target.nil?
472
+
473
+ attributes = target.is_a?(String) ? { "filepath" => target } : {}
474
+ return Xlsxrb.in_span("Xlsxrb.write", attributes: attributes) do
475
+ stream_writer = StreamWriter.new(target, strict_excel_mode: strict_excel_mode)
476
+ begin
477
+ yield stream_writer
478
+ stream_writer.close
479
+ ensure
480
+ stream_writer.cleanup!
481
+ end
482
+ end
483
+ end
484
+
485
+ if workbook_or_nil.nil?
486
+ wb = target_or_workbook
487
+ raise Error, "workbook must be an Elements::Workbook" unless wb.is_a?(Elements::Workbook)
488
+
489
+ io = StringIO.new
490
+ io.binmode
491
+ write(io, wb)
492
+ return io.string.b
493
+ end
494
+
495
+ target = target_or_workbook
496
+ workbook = workbook_or_nil
393
497
  raise Error, "target is required" if target.nil?
394
498
  raise Error, "workbook must be an Elements::Workbook" unless workbook.is_a?(Elements::Workbook)
395
499
 
@@ -399,7 +503,8 @@ module Xlsxrb
399
503
  sst_index = {}
400
504
 
401
505
  # Collect shared strings and build index without allocating new Hashes
402
- sheet_data = workbook.sheets.map do |ws|
506
+ sheet_data = workbook.sheets.map do |raw_ws|
507
+ ws = raw_ws.respond_to?(:load) ? raw_ws.load : raw_ws
403
508
  ws.rows.each do |row|
404
509
  row.cells.each do |cell|
405
510
  val = cell.value
@@ -448,10 +553,10 @@ module Xlsxrb
448
553
  # The block receives an Elements::Workbook and must return a modified one (e.g. via `update_sheet`).
449
554
  # If no target is given, the source is overwritten.
450
555
  #
451
- # @example
452
- # Xlsxrb.modify("template.xlsx", "output.xlsx") do |wb|
453
- # wb.update_sheet(0) do |sheet|
454
- # sheet.update_cell("B1", value: "Updated")
556
+ # @example Modify a template and save to new file
557
+ # Xlsxrb.modify("template.xlsx", "output.xlsx") do |workbook|
558
+ # workbook.update_sheet("Sheet1") do |sheet|
559
+ # sheet.update_cell("B1", value: "Updated Title")
455
560
  # .update_cell("B2", value: 100)
456
561
  # end
457
562
  # end
@@ -463,12 +568,12 @@ module Xlsxrb
463
568
  # @yieldreturn [Elements::Workbook] The modified workbook.
464
569
  # @return [void]
465
570
  # @api public
466
- #: (untyped source, ?untyped target) ?{ (untyped) -> untyped } -> void
571
+ #: (untyped source, ?untyped target) ?{ (Elements::Workbook) -> untyped } -> void
467
572
  def self.modify(source, target = nil)
468
573
  raise Error, "source is required" if source.nil?
469
574
  raise Error, "block is required" unless block_given?
470
575
 
471
- workbook = read(source)
576
+ workbook = read(source).load
472
577
  result_workbook = yield workbook
473
578
  result_workbook = workbook unless result_workbook.is_a?(Elements::Workbook)
474
579
 
@@ -476,25 +581,58 @@ module Xlsxrb
476
581
  write(write_target, result_workbook)
477
582
  end
478
583
 
479
- # Represents a sheet being streamed sequentially.
584
+ # Represents a sheet being streamed sequentially from an XLSX file.
585
+ # Provides O(1) constant-memory streaming over rows and cells.
586
+ #
587
+ # Call #load (or #to_worksheet) to convert this streaming sheet into an
588
+ # in-memory Elements::Worksheet supporting coordinate random access (sheet["A1"]).
589
+ #
590
+ # @example Iterate rows and cells in streaming mode (O(1) memory)
591
+ # Xlsxrb.read("large_data.xlsx") do |sheet|
592
+ # puts "Processing sheet: #{sheet.name}"
593
+ # sheet.each_row do |row|
594
+ # row.each_cell do |cell|
595
+ # puts "#{cell.ref}: #{cell.value}"
596
+ # end
597
+ # end
598
+ # end
599
+ #
600
+ # @example Load into an in-memory Worksheet for coordinate random access
601
+ # wb = Xlsxrb.read("data.xlsx")
602
+ # doc_sheet = wb.sheet(0).load
603
+ # puts doc_sheet["A1"].value
604
+ #
605
+ # @api public
480
606
  class StreamSheet
481
607
  [Enumerable].each { |m| include m }
482
608
 
483
609
  attr_reader :name
484
610
 
485
- def initialize(name, sheet_xml, shared_strings)
611
+ # @param name [String] The sheet name.
612
+ # @param sheet_xml [String] Raw XML content of the sheet.
613
+ # @param shared_strings [Array<String>] Shared strings table.
614
+ # @param styles [Hash, nil] Styles table.
615
+ #: (String name, String sheet_xml, Array[String] shared_strings, ?Hash[untyped, untyped]? styles) -> void
616
+ def initialize(name, sheet_xml, shared_strings, styles = nil)
486
617
  @name = name
487
618
  @sheet_xml = sheet_xml
488
619
  @shared_strings = shared_strings
620
+ @styles = styles
489
621
  end
490
622
 
491
- #: () { (Elements::Row) -> void } -> void
492
- #: | () -> Enumerator[Elements::Row, void]
623
+ # Iterate over rows in this streaming sheet (O(1) memory).
624
+ #
625
+ # @yield [row]
626
+ # @yieldparam row [StreamRow, Elements::Row]
627
+ # @return [Enumerator, void]
628
+ # @api public
629
+ #: () { (StreamRow | Elements::Row) -> void } -> void
630
+ #: | () -> Enumerator[StreamRow | Elements::Row, void]
493
631
  def each_row
494
632
  return enum_for(:each_row) unless block_given?
495
633
 
496
634
  Ooxml::WorksheetParser.each_row(@sheet_xml, shared_strings: @shared_strings) do |row|
497
- if row.is_a?(Elements::Row)
635
+ if row.is_a?(Elements::Row) || row.is_a?(StreamRow)
498
636
  yield row
499
637
  else
500
638
  yield Xlsxrb.send(:build_row_from_raw, row)
@@ -502,76 +640,63 @@ module Xlsxrb
502
640
  end
503
641
  end
504
642
 
505
- #: () { (Elements::Row) -> void } -> void
506
- #: | () -> Enumerator[Elements::Row, void]
507
- def each(&)
508
- each_row(&)
509
- end
510
- end
511
-
512
- # Streaming read: yields StreamSheet objects one at a time for each sheet.
513
- #
514
- # @param source [String, IO] File path or IO object.
515
- # @yield [sheet] Yields each sheet.
516
- # @yieldparam sheet [StreamSheet] The streaming sheet object.
517
- # @return [Enumerator] If no block is given.
518
- # @return [void]
519
- # @api public
520
- #: (untyped source) ?{ (StreamSheet) -> void } -> untyped
521
- def self.foreach(source)
522
- return enum_for(:foreach, source) unless block_given?
523
-
524
- attributes = source.is_a?(String) ? { "filepath" => source } : {}
525
- Xlsxrb.in_span("Xlsxrb.foreach", attributes: attributes) do
526
- entries = Ooxml::ZipReader.open(source, &:read_all)
527
- shared_strings = Ooxml::SharedStringsParser.parse(entries["xl/sharedStrings.xml"])
528
- workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"])
529
- rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"])
530
-
531
- workbook_sheets.each do |sheet_info|
532
- target = rels[sheet_info[:r_id]]
533
- next unless target
534
-
535
- sheet_path = target.start_with?("/") ? target.delete_prefix("/") : "xl/#{target}"
536
- sheet_xml = entries[sheet_path]
537
- next if sheet_xml.nil? || sheet_xml.empty?
643
+ # Iterate over all cells across rows continuously (O(1) memory).
644
+ #
645
+ # @yield [cell]
646
+ # @yieldparam cell [Elements::Cell]
647
+ # @return [Enumerator, void]
648
+ # @api public
649
+ #: () { (Elements::Cell) -> void } -> void
650
+ #: | () -> Enumerator[Elements::Cell, void]
651
+ def each_cell(&)
652
+ return enum_for(:each_cell) unless block_given?
538
653
 
539
- yield StreamSheet.new(sheet_info[:name], sheet_xml, shared_strings)
654
+ each_row do |row|
655
+ row.each_cell(&)
540
656
  end
541
657
  end
542
- end
543
658
 
544
- # Streaming write: yields a StreamWriter context for building XLSX on-the-fly.
545
- #
546
- # @param target [String, IO] File path or IO object.
547
- # @yield [stream_writer]
548
- # @yieldparam stream_writer [Xlsxrb::StreamWriter]
549
- # @return [void]
550
- # @api public
551
- #: (untyped target, ?strict_excel_mode: bool) ?{ (Xlsxrb::StreamWriter) -> void } -> void
552
- def self.generate(target, strict_excel_mode: true)
553
- raise Error, "target is required" if target.nil?
554
- raise Error, "block is required" unless block_given?
659
+ # Default Enumerable iteration iterates rows in the streaming sheet.
660
+ #
661
+ # @yield [row]
662
+ # @yieldparam row [StreamRow, Elements::Row]
663
+ # @return [Enumerator, void]
664
+ # @api public
665
+ #: () { (StreamRow | Elements::Row) -> void } -> void
666
+ #: | () -> Enumerator[StreamRow | Elements::Row, void]
667
+ def each(&)
668
+ each_row(&)
669
+ end
555
670
 
556
- attributes = target.is_a?(String) ? { "filepath" => target } : {}
557
- Xlsxrb.in_span("Xlsxrb.generate", attributes: attributes) do
558
- stream_writer = StreamWriter.new(target, strict_excel_mode: strict_excel_mode)
559
- begin
560
- yield stream_writer
561
- stream_writer.close
562
- ensure
563
- stream_writer.cleanup!
564
- end
671
+ # Loads this sheet completely into an in-memory Elements::Worksheet,
672
+ # enabling coordinate random access (sheet["A1"]), row lookups (row_at),
673
+ # and immutable cell updates (update_cell).
674
+ #
675
+ # @return [Elements::Worksheet] The fully parsed in-memory worksheet.
676
+ # @api public
677
+ #: () -> Elements::Worksheet
678
+ def load
679
+ Xlsxrb.send(:build_worksheet, @name, @sheet_xml, @shared_strings, @styles)
565
680
  end
681
+ alias to_worksheet load
566
682
  end
567
683
 
568
- # Builds an Elements::Workbook in memory using a DSL.
684
+ # Builds an in-memory Elements::Workbook using a declarative DSL.
685
+ #
686
+ # @example Build in-memory workbook
687
+ # workbook = Xlsxrb.build do |builder|
688
+ # builder.sheet("Overview") do |sheet|
689
+ # sheet.row(["Title", "Date"])
690
+ # sheet.row(["Report", Date.today])
691
+ # end
692
+ # end
569
693
  #
694
+ # @param strict_excel_mode [Boolean] Whether to enforce Excel specifications.
570
695
  # @yield [builder]
571
696
  # @yieldparam builder [Xlsxrb::WorkbookBuilder]
572
697
  # @return [Elements::Workbook]
573
698
  # @api public
574
- #: (?strict_excel_mode: bool) ?{ (WorkbookBuilder) -> void } -> untyped
699
+ #: (?strict_excel_mode: bool) ?{ (WorkbookBuilder) -> void } -> Elements::Workbook
575
700
  def self.build(strict_excel_mode: true)
576
701
  raise Error, "block is required" unless block_given?
577
702
 
@@ -741,8 +866,11 @@ module Xlsxrb
741
866
  @custom_properties << { name: name, value: value, type: type }
742
867
  end
743
868
 
869
+ # Builds and returns the in-memory Elements::Workbook.
870
+ #
871
+ # @return [Elements::Workbook]
744
872
  # @api public
745
- #: () -> untyped
873
+ #: () -> Elements::Workbook
746
874
  def build
747
875
  raise ArgumentError, "Workbook must contain at least one sheet (Excel limitation)" if @strict_excel_mode && @sheets.empty?
748
876
 
@@ -1232,7 +1360,7 @@ module Xlsxrb
1232
1360
  # @param items [Array, nil] Items configuration.
1233
1361
  # @return [void]
1234
1362
  # @api public
1235
- #: (untyped source_ref, **untyped opts) -> void
1363
+ #: (untyped source_ref, row_fields: untyped, data_fields: untyped, ?col_fields: untyped, ?dest_ref: untyped, ?name: untyped, ?field_names: untyped, ?items: untyped, **untyped opts) -> void
1236
1364
  def pivot_table(source_ref, row_fields:, data_fields:, col_fields: [], dest_ref: "E1", name: nil, field_names: nil, items: nil)
1237
1365
  @pivot_tables ||= []
1238
1366
  @pivot_tables << {
@@ -1266,7 +1394,7 @@ module Xlsxrb
1266
1394
  # @param opts [Hash] Additional options.
1267
1395
  # @return [void]
1268
1396
  # @api public
1269
- #: (**untyped opts) -> void
1397
+ #: (sparklines: untyped, ?type: untyped, **untyped opts) -> void
1270
1398
  def sparkline_group(sparklines:, type: nil, **opts)
1271
1399
  group = { sparklines: sparklines }
1272
1400
  group[:type] = type if type
@@ -1478,8 +1606,11 @@ module Xlsxrb
1478
1606
  @col_breaks << col_index
1479
1607
  end
1480
1608
 
1609
+ # Builds and returns the in-memory Elements::Worksheet.
1610
+ #
1611
+ # @return [Elements::Worksheet]
1481
1612
  # @api public
1482
- #: () -> untyped
1613
+ #: () -> Elements::Worksheet
1483
1614
  def build
1484
1615
  facade_meta = {}
1485
1616
  facade_meta[:hyperlinks] = @hyperlinks unless @hyperlinks.empty?
@@ -1615,439 +1746,715 @@ module Xlsxrb
1615
1746
  @sheet_name = sheet_name
1616
1747
  end
1617
1748
 
1618
- # Delegates to StreamWriter#style.
1619
- # @see StreamWriter#style
1749
+ # Define or configure a named cell style.
1750
+ #
1751
+ # @example
1752
+ # s.style(:header, bold: true, fill_color: "4F81BD", font_color: "FFFFFF")
1753
+ #
1754
+ # @param name [String, Symbol] The name of the style.
1755
+ # @param opts [Hash] Style options (e.g. bold: true, fill_color: "FF0000").
1756
+ # @yield [style_builder]
1757
+ # @yieldparam style_builder [Xlsxrb::StyleBuilder]
1758
+ # @return [Xlsxrb::StyleBuilder]
1620
1759
  # @api public
1621
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1622
- #: (*untyped args, **untyped kwargs) ?{ (Xlsxrb::StyleBuilder) -> void } -> untyped
1760
+ #: (String | Symbol name, **untyped opts) ?{ (Xlsxrb::StyleBuilder) -> void } -> Xlsxrb::StyleBuilder
1623
1761
  def style(...)
1624
1762
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1625
1763
 
1626
1764
  @writer.style(...)
1627
1765
  end
1628
1766
 
1629
- # Delegates to StreamWriter#merge.
1630
- # @see StreamWriter#merge
1767
+ # Merge a range of cells.
1768
+ #
1769
+ # @example Merge with cell reference string
1770
+ # s.merge("A1:C1")
1771
+ #
1772
+ # @example Merge with coordinates
1773
+ # s.merge(row: 0, col_start: 0, col_end: 2)
1774
+ #
1775
+ # @param range [String, nil] The cell range (e.g. "A1:B2").
1776
+ # @param row [Integer, nil] 0-based row index.
1777
+ # @param col_start [Integer, String, nil] 0-based start column index or letter.
1778
+ # @param col_end [Integer, String, nil] 0-based end column index or letter.
1779
+ # @param row_start [Integer, nil] 0-based start row index.
1780
+ # @param row_end [Integer, nil] 0-based end row index.
1781
+ # @return [void]
1631
1782
  # @api public
1632
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1783
+ #: (?String? range, ?row: Integer | nil, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer | nil, ?row_end: Integer | nil) -> void
1633
1784
  def merge(...)
1634
1785
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1635
1786
 
1636
1787
  @writer.merge(...)
1637
1788
  end
1638
1789
 
1639
- # Delegates to StreamWriter#shape.
1640
- # @see StreamWriter#shape
1790
+ # Add a drawing shape to the sheet.
1791
+ #
1792
+ # @example
1793
+ # s.shape(preset: "ellipse", text: "Circle", from_col: 1, from_row: 1, to_col: 4, to_row: 5)
1794
+ #
1795
+ # @param preset [String] Preset shape type (e.g. "rect", "ellipse").
1796
+ # @param text [String, nil] Shape label text.
1797
+ # @param from_col [Integer] Starting column index (0-based).
1798
+ # @param from_row [Integer] Starting row index (0-based).
1799
+ # @param to_col [Integer] Ending column index (0-based).
1800
+ # @param to_row [Integer] Ending row index (0-based).
1801
+ # @param opts [Hash] Additional shape formatting options.
1802
+ # @return [void]
1641
1803
  # @api public
1642
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1804
+ #: (?preset: String, ?text: String?, ?from_col: Integer, ?from_row: Integer, ?to_col: Integer, ?to_row: Integer, **untyped opts) -> void
1643
1805
  def shape(...)
1644
1806
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1645
1807
 
1646
1808
  @writer.shape(...)
1647
1809
  end
1648
1810
 
1649
- # Delegates to StreamWriter#internal_sheet_setup.
1650
- # @see StreamWriter#internal_sheet_setup
1651
- # @api public
1811
+ # simplecov:disable
1812
+ # Edge case / untested delegation block
1652
1813
  #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1653
1814
  def internal_sheet_setup(...)
1654
1815
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1655
1816
 
1656
- # simplecov:disable
1657
- # Edge case / untested delegation block
1658
1817
  @writer.internal_sheet_setup(...)
1659
- # simplecov:enable
1660
1818
  end
1819
+ # simplecov:enable
1661
1820
 
1662
- # Delegates to StreamWriter#row.
1663
- # @see StreamWriter#row
1821
+ # Add a row to the active sheet.
1822
+ #
1823
+ # @example Write an array of values
1824
+ # s.row(["Name", "Age", "City"])
1825
+ #
1826
+ # @example Write with explicit column keys and styles
1827
+ # s.row({ A: "Header", C: 100 }, styles: { A: :bold })
1828
+ #
1829
+ # @param values [Array, Hash] The cell values (e.g. `[1, 2, 3]` or `{ A: 1, C: 3 }`).
1830
+ # @param styles [String, Symbol, Array, Hash, nil] Style names or hashes to apply.
1831
+ # @param height [Float, Integer, nil] The row height in points (0 - 409).
1832
+ # @param hidden [Boolean] Whether the row is hidden.
1833
+ # @param custom_height [Boolean] Whether to flag as custom height.
1834
+ # @param outline_level [Integer, nil] Grouping/outline level (0 - 7).
1835
+ # @return [void]
1664
1836
  # @api public
1665
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1837
+ #: (Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
1666
1838
  def row(...)
1667
1839
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1668
1840
 
1669
1841
  @writer.row(...)
1670
1842
  end
1671
1843
 
1672
- # Delegates to StreamWriter#column.
1673
- # @see StreamWriter#column
1844
+ # Configure column width and properties.
1845
+ #
1846
+ # @example Set column A width
1847
+ # s.column(0, width: 25.0)
1848
+ #
1849
+ # @param col_index [Integer, String, Symbol] 0-based column index or letter (e.g. 0 or "A" or :A).
1850
+ # @param width [Float, Integer, nil] Column width in characters.
1851
+ # @param hidden [Boolean] Whether the column is hidden.
1852
+ # @param best_fit [Boolean] Whether the column automatically fits content.
1853
+ # @param custom_width [Boolean] Whether to flag as custom width.
1854
+ # @param outline_level [Integer, nil] Grouping/outline level (0 - 7).
1855
+ # @param collapsed [Boolean] Whether the outline group is collapsed.
1856
+ # @return [void]
1674
1857
  # @api public
1675
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1858
+ #: (Integer | String | Symbol col_index, ?width: Float | Integer | nil, ?hidden: bool, ?best_fit: bool, ?custom_width: bool, ?outline_level: Integer | nil, ?collapsed: bool) -> void
1676
1859
  def column(...)
1677
1860
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1678
1861
 
1679
1862
  @writer.column(...)
1680
1863
  end
1681
1864
 
1682
- # Delegates to StreamWriter#chart.
1683
- # @see StreamWriter#chart
1865
+ # Add a chart to the sheet.
1866
+ #
1867
+ # @example
1868
+ # s.chart(:bar) do |chart_builder|
1869
+ # chart_builder.title("Quarterly Sales")
1870
+ # chart_builder.series(values: "Sheet1!$B$2:$B$5", categories: "Sheet1!$A$2:$A$5", name: "Revenue")
1871
+ # end
1872
+ #
1873
+ # @param type [Symbol, String, nil] The chart type (:bar, :col, :line, :pie, :scatter, :area, :doughnut, :radar).
1874
+ # @param opts [Hash] Additional chart options.
1875
+ # @yield [chart_builder]
1876
+ # @yieldparam chart_builder [Xlsxrb::ChartBuilder]
1877
+ # @return [void]
1684
1878
  # @api public
1685
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1879
+ #: (?Symbol | String? type, **untyped opts) ?{ (Xlsxrb::ChartBuilder) -> void } -> void
1686
1880
  def chart(...)
1687
1881
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1688
1882
 
1689
1883
  @writer.chart(...)
1690
1884
  end
1691
1885
 
1692
- # Delegates to StreamWriter#hyperlink.
1693
- # @see StreamWriter#hyperlink
1886
+ # Add a hyperlink to a cell.
1887
+ #
1888
+ # @example Positional URL
1889
+ # s.hyperlink("A1", "https://example.com", display: "Example")
1890
+ #
1891
+ # @example Keyword location
1892
+ # s.hyperlink("A1", location: "https://example.com", tooltip: "Go to Example")
1893
+ #
1894
+ # @param cell [String] The cell reference (e.g. "A1").
1895
+ # @param url [String, nil] The target URL or URI.
1896
+ # @param display [String, nil] Display text for the link.
1897
+ # @param tooltip [String, nil] Tooltip text when hovering.
1898
+ # @param location [String, nil] Destination location / URL (keyword alternative).
1899
+ # @return [void]
1694
1900
  # @api public
1695
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1901
+ #: (String cell, ?String? url, ?display: String?, ?tooltip: String?, ?location: String?) -> void
1696
1902
  def hyperlink(...)
1697
1903
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1698
1904
 
1699
1905
  @writer.hyperlink(...)
1700
1906
  end
1701
1907
 
1702
- # Delegates to StreamWriter#auto_filter.
1703
- # @see StreamWriter#auto_filter
1908
+ # Set the auto-filter range on the sheet.
1909
+ #
1910
+ # @example
1911
+ # s.auto_filter("A1:D100")
1912
+ #
1913
+ # @param ref [String] The cell range (e.g. "A1:D10").
1914
+ # @return [void]
1704
1915
  # @api public
1705
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1916
+ #: (String ref) -> void
1706
1917
  def auto_filter(...)
1707
1918
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1708
1919
 
1709
1920
  @writer.auto_filter(...)
1710
1921
  end
1711
1922
 
1712
- # Delegates to StreamWriter#filter_column.
1713
- # @see StreamWriter#filter_column
1923
+ # Set filter criteria for a column in the auto-filter.
1924
+ #
1925
+ # @example Simple values filter
1926
+ # s.filter_column(0, ["Active", "Pending"])
1927
+ #
1928
+ # @example Custom filter specification
1929
+ # s.filter_column(0, { type: :filters, values: ["Data"] })
1930
+ #
1931
+ # @param col_id [Integer] 0-based column index relative to auto-filter range.
1932
+ # @param filter_values [Array<String>, Hash] Values or filter specification hash.
1933
+ # @return [void]
1714
1934
  # @api public
1715
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1935
+ #: (Integer col_id, Array[String] | Hash[Symbol, untyped] filter_values) -> void
1716
1936
  def filter_column(...)
1717
1937
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1718
1938
 
1719
1939
  @writer.filter_column(...)
1720
1940
  end
1721
1941
 
1722
- # Delegates to StreamWriter#sort_state.
1723
- # @see StreamWriter#sort_state
1942
+ # Configure sort state on a range.
1943
+ #
1944
+ # @example
1945
+ # s.sort_state("A1:A10", [{ ref: "A1:A10", descending: true }])
1946
+ #
1947
+ # @param ref [String] The range to sort.
1948
+ # @param sort_conditions [Array<Hash>, Hash] Sort conditions array or options hash.
1949
+ # @param opts [Hash] Additional sort options.
1950
+ # @return [void]
1724
1951
  # @api public
1725
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1952
+ #: (String ref, Array[Hash[Symbol, untyped]] | Hash[Symbol, untyped] sort_conditions, **untyped opts) -> void
1726
1953
  def sort_state(...)
1727
1954
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1728
1955
 
1729
1956
  @writer.sort_state(...)
1730
1957
  end
1731
1958
 
1732
- # Delegates to StreamWriter#validate_data.
1733
- # @see StreamWriter#validate_data
1959
+ # Add data validation rules to a range.
1960
+ #
1961
+ # @example Dropdown list validation
1962
+ # s.validate_data("B2:B100", type: "list", formula1: '"High,Medium,Low"')
1963
+ #
1964
+ # @example Integer range validation
1965
+ # s.validate_data("C2:C100", type: "whole", operator: "between", formula1: 1, formula2: 100)
1966
+ #
1967
+ # @param range [String] The cell range (e.g. "B2:B10").
1968
+ # @param type [String, Symbol] Validation type ("list", "whole", "decimal", "date", "time", "textLength", "custom").
1969
+ # @param opts [Hash] Validation options.
1970
+ # @return [void]
1734
1971
  # @api public
1735
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1972
+ #: (String range, ?type: String | Symbol, **untyped opts) -> void
1736
1973
  def validate_data(...)
1737
1974
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1738
1975
 
1739
1976
  @writer.validate_data(...)
1740
1977
  end
1741
1978
 
1742
- # Delegates to StreamWriter#conditional_format.
1743
- # @see StreamWriter#conditional_format
1979
+ # Add conditional formatting to a range.
1980
+ #
1981
+ # @example Highlight values greater than 100
1982
+ # s.conditional_format("A1:A10", type: "cellIs", operator: "greaterThan", formula: 100, style: :highlight)
1983
+ #
1984
+ # @param range [String] The cell range (e.g. "A1:A10").
1985
+ # @param type [String, Symbol] Rule type ("cellIs", "colorScale", "dataBar", "expression").
1986
+ # @param opts [Hash] Rule options.
1987
+ # @return [void]
1744
1988
  # @api public
1745
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
1989
+ #: (String range, ?type: String | Symbol, **untyped opts) -> void
1746
1990
  def conditional_format(...)
1747
1991
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1748
1992
 
1749
1993
  @writer.conditional_format(...)
1750
1994
  end
1751
1995
 
1752
- # Delegates to StreamWriter#table.
1753
- # @see StreamWriter#table
1996
+ # Add a formatted Excel Table to the sheet.
1997
+ #
1998
+ # @example
1999
+ # s.table("A1:C10", columns: ["ID", "Name", "Total"], name: "SalesTable", style: "TableStyleMedium9")
2000
+ #
2001
+ # @param ref [String] The cell range for the table (e.g. "A1:D10").
2002
+ # @param columns [Array<String>, Array<Hash>] Column names or definitions.
2003
+ # @param name [String, nil] Table name.
2004
+ # @param display_name [String, nil] Display name.
2005
+ # @param style [String, nil] Table style name.
2006
+ # @param opts [Hash] Additional options.
2007
+ # @return [void]
1754
2008
  # @api public
1755
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2009
+ #: (String ref, columns: untyped, ?name: String?, ?display_name: String?, ?style: String?, **untyped opts) -> void
1756
2010
  def table(...)
1757
2011
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1758
2012
 
1759
2013
  @writer.table(...)
1760
2014
  end
1761
2015
 
1762
- # Delegates to StreamWriter#cleanup!.
1763
- # @see StreamWriter#cleanup!
1764
- # @api public
1765
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2016
+ # simplecov:disable
2017
+ # Edge case / untested delegation block
2018
+ #: () -> void
1766
2019
  def cleanup!(...)
1767
2020
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1768
2021
 
1769
- # simplecov:disable
1770
- # Edge case / untested delegation block
1771
2022
  @writer.cleanup!(...)
1772
- # simplecov:enable
1773
2023
  end
2024
+ # simplecov:enable
1774
2025
 
1775
- # Delegates to StreamWriter#comment.
1776
- # @see StreamWriter#comment
2026
+ # Add a comment to a cell.
2027
+ #
2028
+ # @example
2029
+ # s.comment("A1", "Reviewed and approved", author: "Auditor")
2030
+ #
2031
+ # @param cell [String, Integer] The cell reference (e.g. "A1").
2032
+ # @param text [String] The comment text.
2033
+ # @param author [String] The author name.
2034
+ # @return [void]
1777
2035
  # @api public
1778
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2036
+ #: (String | Integer cell, String text, ?author: String) -> void
1779
2037
  def comment(...)
1780
2038
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1781
2039
 
1782
2040
  @writer.comment(...)
1783
2041
  end
1784
2042
 
1785
- # Delegates to StreamWriter#pivot_table.
1786
- # @see StreamWriter#pivot_table
2043
+ # Add a Pivot Table to the sheet.
2044
+ #
2045
+ # @example
2046
+ # s.pivot_table("Sheet1!A1:D100", row_fields: ["Category"], data_fields: ["Amount"], dest_ref: "F1")
2047
+ #
2048
+ # @param source_ref [String] Source data range reference (e.g. "Sheet1!A1:D100").
2049
+ # @param row_fields [Array<String>] Field names for rows.
2050
+ # @param data_fields [Array<String>] Field names for data values.
2051
+ # @param col_fields [Array<String>] Field names for columns.
2052
+ # @param dest_ref [String] Target top-left cell reference (default: "E1").
2053
+ # @param name [String, nil] Pivot table name.
2054
+ # @param field_names [Array<String>, nil] Override field names.
2055
+ # @param items [Array, nil] Items configuration.
2056
+ # @param opts [Hash] Additional options.
2057
+ # @return [void]
1787
2058
  # @api public
1788
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2059
+ #: (String source_ref, row_fields: untyped, data_fields: untyped, ?col_fields: untyped, ?dest_ref: String, ?name: String?, ?field_names: untyped, ?items: untyped, **untyped opts) -> void
1789
2060
  def pivot_table(...)
1790
2061
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1791
2062
 
1792
2063
  @writer.pivot_table(...)
1793
2064
  end
1794
2065
 
1795
- # Delegates to StreamWriter#sparkline_group.
1796
- # @see StreamWriter#sparkline_group
2066
+ # Add sparklines to the sheet.
2067
+ #
2068
+ # @example
2069
+ # s.sparkline_group(sparklines: [{ data_ref: "A1:E1", location_ref: "F1" }], type: "line")
2070
+ #
2071
+ # @param sparklines [Array<Hash>] Array of { data_ref:, location_ref: } hashes.
2072
+ # @param type [String, nil] "line" (default), "column", or "stacked".
2073
+ # @param opts [Hash] Additional sparkline options.
2074
+ # @return [void]
1797
2075
  # @api public
1798
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2076
+ #: (sparklines: Array[Hash[Symbol, untyped]], ?type: String?, **untyped opts) -> void
1799
2077
  def sparkline_group(...)
1800
2078
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1801
2079
 
1802
2080
  @writer.sparkline_group(...)
1803
2081
  end
1804
2082
 
1805
- # Delegates to StreamWriter#workbook_property.
1806
- # @see StreamWriter#workbook_property
2083
+ # Set workbook-level properties.
2084
+ #
2085
+ # @param opts [Hash] Workbook property options.
2086
+ # @return [void]
1807
2087
  # @api public
1808
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2088
+ #: (**untyped opts) -> void
1809
2089
  def workbook_property(...)
1810
2090
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1811
2091
 
1812
2092
  @writer.workbook_property(...)
1813
2093
  end
1814
2094
 
1815
- # Delegates to StreamWriter#sheet_properties.
1816
- # @see StreamWriter#sheet_properties
2095
+ # Set sheet properties (e.g. tab color, page setup flags).
2096
+ #
2097
+ # @example
2098
+ # s.sheet_properties(:tab_color, "FF0000")
2099
+ #
2100
+ # @param name [Symbol, String] Property name.
2101
+ # @param value [Object] Property value.
2102
+ # @return [void]
1817
2103
  # @api public
1818
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2104
+ #: (Symbol | String name, untyped value) -> void
1819
2105
  def sheet_properties(...)
1820
2106
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1821
2107
 
1822
2108
  @writer.sheet_properties(...)
1823
2109
  end
1824
2110
 
1825
- # Delegates to StreamWriter#defined_name.
1826
- # @see StreamWriter#defined_name
2111
+ # Add a defined named range or formula.
2112
+ #
2113
+ # @example
2114
+ # s.defined_name("TaxRate", "0.10")
2115
+ #
2116
+ # @param name [String] The name.
2117
+ # @param formula [String] The formula or range expression.
2118
+ # @param sheet_id [Integer, nil] Optional sheet scope.
2119
+ # @param hidden [Boolean] Whether the name is hidden.
2120
+ # @return [void]
1827
2121
  # @api public
1828
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2122
+ #: (String name, String formula, ?sheet_id: Integer | nil, ?hidden: bool) -> void
1829
2123
  def defined_name(...)
1830
2124
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1831
2125
 
1832
2126
  @writer.defined_name(...)
1833
2127
  end
1834
2128
 
1835
- # Delegates to StreamWriter#freeze_pane.
1836
- # @see StreamWriter#freeze_pane
2129
+ # Freeze rows and/or columns for scrolling.
2130
+ #
2131
+ # @example Freeze top row
2132
+ # s.freeze_pane(row: 1)
2133
+ #
2134
+ # @example Freeze first column and top 2 rows
2135
+ # s.freeze_pane(row: 2, col: 1)
2136
+ #
2137
+ # @param row [Integer, nil] Number of rows to freeze.
2138
+ # @param col [Integer, nil] Number of columns to freeze.
2139
+ # @return [void]
1837
2140
  # @api public
1838
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2141
+ #: (?row: Integer | nil, ?col: Integer | nil) -> void
1839
2142
  def freeze_pane(...)
1840
2143
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1841
2144
 
1842
2145
  @writer.freeze_pane(...)
1843
2146
  end
1844
2147
 
1845
- # Delegates to StreamWriter#print_area.
1846
- # @see StreamWriter#print_area
2148
+ # simplecov:disable
2149
+ # Edge case / untested delegation block
2150
+ # Set the print area range for the sheet.
2151
+ #
2152
+ # @example
2153
+ # s.print_area("A1:G50")
2154
+ #
2155
+ # @param ref [String] Range reference.
2156
+ # @return [void]
1847
2157
  # @api public
1848
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2158
+ #: (String ref) -> void
1849
2159
  def print_area(...)
1850
- # simplecov:disable
1851
- # Edge case / untested delegation block
1852
2160
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1853
2161
 
1854
2162
  @writer.print_area(...)
1855
- # simplecov:enable
1856
2163
  end
2164
+ # simplecov:enable
1857
2165
 
1858
- # Delegates to StreamWriter#print_titles.
1859
- # @see StreamWriter#print_titles
2166
+ # simplecov:disable
2167
+ # Edge case / untested delegation block
2168
+ # Configure repeating title rows and columns for printing.
2169
+ #
2170
+ # @example Repeat top 2 rows on every page
2171
+ # s.print_titles(rows: "1:2")
2172
+ #
2173
+ # @param rows [String, nil] Row range to repeat (e.g. "1:2").
2174
+ # @param cols [String, nil] Column range to repeat (e.g. "A:B").
2175
+ # @return [void]
1860
2176
  # @api public
1861
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2177
+ #: (?rows: String?, ?cols: String?) -> void
1862
2178
  def print_titles(...)
1863
- # simplecov:disable
1864
- # Edge case / untested delegation block
1865
2179
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1866
2180
 
1867
2181
  @writer.print_titles(...)
1868
- # simplecov:enable
1869
2182
  end
2183
+ # simplecov:enable
1870
2184
 
1871
- # Delegates to StreamWriter#split_pane.
1872
- # @see StreamWriter#split_pane
2185
+ # Split sheet view into panes.
2186
+ #
2187
+ # @param x_split [Numeric, nil] Horizontal split position.
2188
+ # @param y_split [Numeric, nil] Vertical split position.
2189
+ # @param top_left_cell [String, nil] Top-left visible cell in bottom-right pane.
2190
+ # @param active_pane [String, nil] Active pane identifier.
2191
+ # @param state [String, nil] Split state.
2192
+ # @return [void]
1873
2193
  # @api public
1874
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2194
+ #: (?x_split: Numeric | nil, ?y_split: Numeric | nil, ?top_left_cell: String?, ?active_pane: String?, ?state: String?) -> void
1875
2195
  def split_pane(...)
1876
2196
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1877
2197
 
1878
2198
  @writer.split_pane(...)
1879
2199
  end
1880
2200
 
1881
- # Delegates to StreamWriter#protect_workbook.
1882
- # @see StreamWriter#protect_workbook
2201
+ # simplecov:disable
2202
+ # Edge case / untested delegation block
2203
+ # Protect the workbook structure.
2204
+ #
2205
+ # @param opts [Hash] Protection options.
2206
+ # @return [void]
1883
2207
  # @api public
1884
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2208
+ #: (**untyped opts) -> void
1885
2209
  def protect_workbook(...)
1886
- # simplecov:disable
1887
- # Edge case / untested delegation block
1888
2210
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1889
2211
 
1890
2212
  @writer.protect_workbook(...)
1891
- # simplecov:enable
1892
2213
  end
2214
+ # simplecov:enable
1893
2215
 
1894
- # Delegates to StreamWriter#core_property.
1895
- # @see StreamWriter#core_property
2216
+ # simplecov:disable
2217
+ # Edge case / untested delegation block
2218
+ # Set core metadata property.
2219
+ #
2220
+ # @param name [String, Symbol] Property name.
2221
+ # @param value [Object] Property value.
2222
+ # @return [void]
1896
2223
  # @api public
1897
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2224
+ #: (String | Symbol name, untyped value) -> void
1898
2225
  def core_property(...)
1899
- # simplecov:disable
1900
- # Edge case / untested delegation block
1901
2226
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1902
2227
 
1903
2228
  @writer.core_property(...)
1904
- # simplecov:enable
1905
2229
  end
2230
+ # simplecov:enable
1906
2231
 
1907
- # Delegates to StreamWriter#select_cell.
1908
- # @see StreamWriter#select_cell
2232
+ # Set the active/selected cell on the sheet.
2233
+ #
2234
+ # @example
2235
+ # s.select_cell("B5")
2236
+ # s.select_cell("A1", sqref: "A1:A2", pane: "topRight")
2237
+ #
2238
+ # @param active_cell [String] Cell reference (e.g. "A1").
2239
+ # @param sqref [String, nil] Selection range.
2240
+ # @param pane [String, Symbol, nil] Pane identifier.
2241
+ # @return [void]
1909
2242
  # @api public
1910
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2243
+ #: (String active_cell, ?sqref: String?, ?pane: (String | Symbol)?) -> void
1911
2244
  def select_cell(...)
1912
2245
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1913
2246
 
1914
2247
  @writer.select_cell(...)
1915
2248
  end
1916
2249
 
1917
- # Delegates to StreamWriter#page_margins.
1918
- # @see StreamWriter#page_margins
2250
+ # Configure page margins for printing.
2251
+ #
2252
+ # @example
2253
+ # s.page_margins(left: 0.7, right: 0.7, top: 0.75, bottom: 0.75)
2254
+ #
2255
+ # @param left [Float, nil] Left margin in inches.
2256
+ # @param right [Float, nil] Right margin in inches.
2257
+ # @param top [Float, nil] Top margin in inches.
2258
+ # @param bottom [Float, nil] Bottom margin in inches.
2259
+ # @param header [Float, nil] Header margin in inches.
2260
+ # @param footer [Float, nil] Footer margin in inches.
2261
+ # @return [void]
1919
2262
  # @api public
1920
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2263
+ #: (?left: Float | nil, ?right: Float | nil, ?top: Float | nil, ?bottom: Float | nil, ?header: Float | nil, ?footer: Float | nil) -> void
1921
2264
  def page_margins(...)
1922
2265
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1923
2266
 
1924
2267
  @writer.page_margins(...)
1925
2268
  end
1926
2269
 
1927
- # Delegates to StreamWriter#page_setup.
1928
- # @see StreamWriter#page_setup
2270
+ # Configure page orientation, paper size, and print setup.
2271
+ #
2272
+ # @example Landscape A4
2273
+ # s.page_setup(orientation: "landscape", paper_size: 9)
2274
+ #
2275
+ # @param orientation [String, Symbol, nil] "portrait" or "landscape" (or :portrait, :landscape).
2276
+ # @param paper_size [Integer, nil] Paper size index (e.g. 9 for A4, 1 for Letter).
2277
+ # @param opts [Hash] Additional options (scale, fit_to_width, fit_to_height).
2278
+ # @return [void]
1929
2279
  # @api public
1930
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2280
+ #: (?orientation: (String | Symbol)?, ?paper_size: Integer | nil, **untyped opts) -> void
1931
2281
  def page_setup(...)
1932
2282
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1933
2283
 
1934
2284
  @writer.page_setup(...)
1935
2285
  end
1936
2286
 
1937
- # Delegates to StreamWriter#header_footer.
1938
- # @see StreamWriter#header_footer
2287
+ # Configure header and footer text for printing.
2288
+ #
2289
+ # @example
2290
+ # s.header_footer(odd_header: "&CConfidential", odd_footer: "&RPage &P of &N")
2291
+ #
2292
+ # @param opts [Hash] Header and footer specifications.
2293
+ # @return [void]
1939
2294
  # @api public
1940
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2295
+ #: (**untyped opts) -> void
1941
2296
  def header_footer(...)
1942
2297
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1943
2298
 
1944
2299
  @writer.header_footer(...)
1945
2300
  end
1946
2301
 
1947
- # Delegates to StreamWriter#print_options.
1948
- # @see StreamWriter#print_options
2302
+ # Configure print options (e.g. gridlines, headings).
2303
+ #
2304
+ # @example
2305
+ # s.print_options(:grid_lines, true)
2306
+ #
2307
+ # @param name [Symbol, String] Print option name.
2308
+ # @param value [Object] Print option value.
2309
+ # @return [void]
1949
2310
  # @api public
1950
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2311
+ #: (Symbol | String name, untyped value) -> void
1951
2312
  def print_options(...)
1952
2313
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1953
2314
 
1954
2315
  @writer.print_options(...)
1955
2316
  end
1956
2317
 
1957
- # Delegates to StreamWriter#properties.
1958
- # @see StreamWriter#properties
2318
+ # simplecov:disable
2319
+ # Edge case / untested delegation block
2320
+ # Set document metadata properties (core, app, custom).
2321
+ #
2322
+ # @example
2323
+ # s.properties(core: { title: "Report", creator: "App" })
2324
+ #
2325
+ # @param core [Hash, nil] Core properties (title, creator, subject, etc.).
2326
+ # @param app [Hash, nil] App properties (company, manager).
2327
+ # @param custom [Hash, nil] Custom properties.
2328
+ # @return [void]
1959
2329
  # @api public
1960
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2330
+ #: (?core: Hash[untyped, untyped]?, ?app: Hash[untyped, untyped]?, ?custom: Hash[untyped, untyped]?) -> void
1961
2331
  def properties(...)
1962
- # simplecov:disable
1963
- # Edge case / untested delegation block
1964
2332
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1965
2333
 
1966
2334
  @writer.properties(...)
1967
- # simplecov:enable
1968
2335
  end
2336
+ # simplecov:enable
1969
2337
 
1970
- # Delegates to StreamWriter#app_property.
1971
- # @see StreamWriter#app_property
2338
+ # simplecov:disable
2339
+ # Edge case / untested delegation block
2340
+ # Set app metadata property.
2341
+ #
2342
+ # @param name [String, Symbol] Property name.
2343
+ # @param value [Object] Property value.
2344
+ # @return [void]
1972
2345
  # @api public
1973
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2346
+ #: (String | Symbol name, untyped value) -> void
1974
2347
  def app_property(...)
1975
- # simplecov:disable
1976
- # Edge case / untested delegation block
1977
2348
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1978
2349
 
1979
2350
  @writer.app_property(...)
1980
- # simplecov:enable
1981
2351
  end
2352
+ # simplecov:enable
1982
2353
 
1983
- # Delegates to StreamWriter#protect_sheet.
1984
- # @see StreamWriter#protect_sheet
2354
+ # Protect the worksheet against modifications.
2355
+ #
2356
+ # @example
2357
+ # s.protect_sheet(password: "secret", select_locked_cells: true)
2358
+ #
2359
+ # @param opts [Hash] Protection options.
2360
+ # @return [void]
1985
2361
  # @api public
1986
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2362
+ #: (**untyped opts) -> void
1987
2363
  def protect_sheet(...)
1988
2364
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
1989
2365
 
1990
2366
  @writer.protect_sheet(...)
1991
2367
  end
1992
2368
 
1993
- # Delegates to StreamWriter#custom_property.
1994
- # @see StreamWriter#custom_property
2369
+ # simplecov:disable
2370
+ # Edge case / untested delegation block
2371
+ # Set custom metadata property.
2372
+ #
2373
+ # @param name [String, Symbol] Property name.
2374
+ # @param value [Object] Property value.
2375
+ # @return [void]
1995
2376
  # @api public
1996
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2377
+ #: (String | Symbol name, untyped value) -> void
1997
2378
  def custom_property(...)
1998
- # simplecov:disable
1999
- # Edge case / untested delegation block
2000
2379
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
2001
2380
 
2002
2381
  @writer.custom_property(...)
2003
- # simplecov:enable
2004
2382
  end
2383
+ # simplecov:enable
2005
2384
 
2006
- # Delegates to StreamWriter#image.
2007
- # @see StreamWriter#image
2385
+ # Insert an image into the sheet.
2386
+ #
2387
+ # @example
2388
+ # s.image(File.read("logo.png"), ext: "png", from_col: 0, from_row: 0, to_col: 2, to_row: 3)
2389
+ #
2390
+ # @param file_data [String] Binary image data or file content.
2391
+ # @param ext [String] Image extension ("png", "jpeg", etc.).
2392
+ # @param from_col [Integer] Starting column index (0-based).
2393
+ # @param from_row [Integer] Starting row index (0-based).
2394
+ # @param to_col [Integer] Ending column index (0-based).
2395
+ # @param to_row [Integer] Ending row index (0-based).
2396
+ # @param opts [Hash] Additional anchor and sizing options.
2397
+ # @return [void]
2008
2398
  # @api public
2009
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2399
+ #: (String file_data, ?ext: String, ?from_col: Integer, ?from_row: Integer, ?to_col: Integer, ?to_row: Integer, **untyped opts) -> void
2010
2400
  def image(...)
2011
2401
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
2012
2402
 
2013
2403
  @writer.image(...)
2014
2404
  end
2015
2405
 
2016
- # Delegates to StreamWriter#sheet_view.
2017
- # @see StreamWriter#sheet_view
2406
+ # Configure sheet view settings (zoom scale, grid lines visibility).
2407
+ #
2408
+ # @example
2409
+ # s.sheet_view(:show_grid_lines, false)
2410
+ # s.sheet_view(:zoom_scale, 120)
2411
+ #
2412
+ # @param name [Symbol, String] View setting name.
2413
+ # @param value [Object] View setting value.
2414
+ # @return [void]
2018
2415
  # @api public
2019
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2416
+ #: (Symbol | String name, untyped value) -> void
2020
2417
  def sheet_view(...)
2021
2418
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
2022
2419
 
2023
2420
  @writer.sheet_view(...)
2024
2421
  end
2025
2422
 
2026
- # Delegates to StreamWriter#page_break_row.
2027
- # @see StreamWriter#page_break_row
2423
+ # simplecov:disable
2424
+ # Edge case / untested delegation block
2425
+ # Add a horizontal page break after the given row index.
2426
+ #
2427
+ # @example
2428
+ # s.page_break_row(25)
2429
+ #
2430
+ # @param row_index [Integer] 0-based row index.
2431
+ # @return [void]
2028
2432
  # @api public
2029
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2433
+ #: (Integer row_index) -> void
2030
2434
  def page_break_row(...)
2031
- # simplecov:disable
2032
- # Edge case / untested delegation block
2033
2435
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
2034
2436
 
2035
2437
  @writer.page_break_row(...)
2036
- # simplecov:enable
2037
2438
  end
2439
+ # simplecov:enable
2038
2440
 
2039
- # Delegates to StreamWriter#page_break_col.
2040
- # @see StreamWriter#page_break_col
2441
+ # simplecov:disable
2442
+ # Edge case / untested delegation block
2443
+ # Add a vertical page break after the given column index.
2444
+ #
2445
+ # @example
2446
+ # s.page_break_col(5)
2447
+ #
2448
+ # @param col_index [Integer] 0-based column index.
2449
+ # @return [void]
2041
2450
  # @api public
2042
- #: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
2451
+ #: (Integer col_index) -> void
2043
2452
  def page_break_col(...)
2044
- # simplecov:disable
2045
- # Edge case / untested delegation block
2046
2453
  raise Error, "Sheet '' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
2047
2454
 
2048
2455
  @writer.page_break_col(...)
2049
- # simplecov:enable
2050
2456
  end
2457
+ # simplecov:enable
2051
2458
  end
2052
2459
 
2053
2460
  # Add a new sheet.
@@ -2315,7 +2722,7 @@ module Xlsxrb
2315
2722
  end
2316
2723
 
2317
2724
  # --- Tables ---
2318
- #: (untyped ref, **untyped opts) -> void
2725
+ #: (untyped ref, columns: untyped, ?name: untyped, ?display_name: untyped, ?style: untyped, **untyped opts) -> void
2319
2726
  def table(ref, columns:, name: nil, display_name: nil, style: nil, **opts)
2320
2727
  sheet if @current_sheet.nil?
2321
2728
  tbl = { ref: ref, columns: columns }
@@ -2327,7 +2734,7 @@ module Xlsxrb
2327
2734
  end
2328
2735
 
2329
2736
  # --- Pivot Tables ---
2330
- #: (untyped source_ref, **untyped opts) -> void
2737
+ #: (untyped source_ref, row_fields: untyped, data_fields: untyped, ?col_fields: untyped, ?dest_ref: untyped, ?name: untyped, ?field_names: untyped, ?items: untyped, **untyped opts) -> void
2331
2738
  def pivot_table(source_ref, row_fields:, data_fields:, col_fields: [], dest_ref: "E1", name: nil, field_names: nil, items: nil)
2332
2739
  sheet if @current_sheet.nil?
2333
2740
  @current_pivot_tables ||= []
@@ -2347,7 +2754,7 @@ module Xlsxrb
2347
2754
  end
2348
2755
 
2349
2756
  # --- Sparklines ---
2350
- #: (**untyped opts) -> void
2757
+ #: (sparklines: untyped, ?type: untyped, **untyped opts) -> void
2351
2758
  def sparkline_group(sparklines:, type: nil, **opts)
2352
2759
  sheet if @current_sheet.nil?
2353
2760
  group = { sparklines: sparklines }