xlsxrb 0.1.7 → 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.
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ module Xlsxrb
6
+ # Streaming row implementation that parses cells on-demand / lazily.
7
+ # Provides O(1) memory consumption even for rows with tens of thousands of columns.
8
+ #
9
+ # @example Streaming cells one-by-one (O(1) memory)
10
+ # row.each_cell do |cell|
11
+ # puts "#{cell.ref}: #{cell.value}"
12
+ # end
13
+ #
14
+ # @example Random access or array conversion (cached on-demand)
15
+ # cell = row[0]
16
+ # values = row.to_a
17
+ #
18
+ # @api public
19
+ class StreamRow
20
+ [Enumerable].each { |m| include m }
21
+
22
+ attr_reader :index, :height, :hidden, :custom_height, :outline_level
23
+
24
+ # @param index [Integer] 0-based row index.
25
+ # @param xml_bytes [String] Raw ASCII-8BIT XML bytes.
26
+ # @param from [Integer] Byte offset where cells start.
27
+ # @param to [Integer] Byte offset where cells end.
28
+ # @param shared_strings [Array<String>] Shared strings table.
29
+ # @param height [Float, Integer, nil] Row height in points.
30
+ # @param hidden [Boolean] Whether the row is hidden.
31
+ # @param custom_height [Boolean] Whether custom height is set.
32
+ # @param outline_level [Integer, nil] Grouping/outline level.
33
+ #: (index: Integer, xml_bytes: String, from: Integer, to: Integer, shared_strings: Array[String], ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
34
+ def initialize(index:, xml_bytes:, from:, to:, shared_strings:, height: nil, hidden: false,
35
+ custom_height: false, outline_level: nil)
36
+ @index = index
37
+ @xml = xml_bytes
38
+ @from = from
39
+ @to = to
40
+ @shared_strings = shared_strings
41
+ @height = height
42
+ @hidden = hidden
43
+ @custom_height = custom_height
44
+ @outline_level = outline_level
45
+ @cells_cache = nil
46
+ end
47
+
48
+ # Iterate over cells in this streaming row one by one.
49
+ #
50
+ # @yield [cell]
51
+ # @yieldparam cell [Elements::Cell]
52
+ # @return [Enumerator, void]
53
+ # @api public
54
+ #: () { (Elements::Cell) -> void } -> void
55
+ #: | () -> Enumerator[Elements::Cell, void]
56
+ def each_cell(&)
57
+ return enum_for(:each_cell) unless block_given?
58
+
59
+ if @cells
60
+ @cells.each(&)
61
+ else
62
+ Ooxml::WorksheetParser.fast_scan_cells_direct(@xml, @from, @to, @shared_strings, { row: @index }, &)
63
+ end
64
+ end
65
+
66
+ # Iterate over cells in this streaming row.
67
+ #
68
+ # @yield [cell]
69
+ # @yieldparam cell [Elements::Cell]
70
+ # @return [Enumerator, void]
71
+ # @api public
72
+ #: () { (Elements::Cell) -> void } -> void
73
+ #: | () -> Enumerator[Elements::Cell, void]
74
+ def each(&)
75
+ each_cell(&)
76
+ end
77
+
78
+ # Returns all cells as an Array. Cached on first access.
79
+ #
80
+ # @return [Array<Elements::Cell>]
81
+ # @api public
82
+ #: () -> Array[Elements::Cell]
83
+ def cells
84
+ @cells ||= each_cell.to_a.freeze
85
+ end
86
+
87
+ # Access a cell by 0-based column index, or access row attributes via Symbol.
88
+ #
89
+ # @param col_index [Integer, Symbol] Column index or attribute symbol.
90
+ # @return [Elements::Cell, Object, nil]
91
+ # @api public
92
+ #: (Integer | Symbol col_index) -> untyped
93
+ def [](col_index)
94
+ case col_index
95
+ when Symbol
96
+ case col_index
97
+ when :cells then cells
98
+ when :index then index
99
+ when :height then height
100
+ when :hidden then hidden
101
+ when :custom_height then custom_height
102
+ when :outline_level then outline_level
103
+ when :attrs then { height: height, hidden: hidden, custom_height: custom_height, outline_level: outline_level }
104
+ end
105
+ else
106
+ cells[col_index]
107
+ end
108
+ end
109
+
110
+ # Access a cell by 0-based column index.
111
+ #
112
+ # @param col_index [Integer] 0-based column index.
113
+ # @return [Elements::Cell, nil]
114
+ # @api public
115
+ #: (Integer col_index) -> Elements::Cell?
116
+ def cell_at(col_index)
117
+ cells.find { |c| c.column_index == col_index }
118
+ end
119
+
120
+ # Convert row cells to an Array of raw values (sparse columns get nil).
121
+ #
122
+ # @return [Array<Object>]
123
+ # @api public
124
+ #: () -> Array[untyped]
125
+ def to_a
126
+ return [] if cells.empty?
127
+
128
+ max_col = cells.map(&:column_index).max || 0
129
+ arr = Array.new(max_col + 1)
130
+ cells.each do |cell|
131
+ arr[cell.column_index] = cell.value
132
+ end
133
+ arr
134
+ end
135
+
136
+ # Returns cell values as an Array.
137
+ #
138
+ # @return [Array<Object>]
139
+ # @api public
140
+ #: () -> Array[untyped]
141
+ def values
142
+ to_a
143
+ end
144
+
145
+ # Returns whether the row is valid according to OOXML specifications.
146
+ #
147
+ # @return [Boolean]
148
+ # @api public
149
+ #: () -> bool
150
+ def valid?
151
+ true
152
+ end
153
+
154
+ # Unmapped metadata for compatibility with Elements::Row.
155
+ #
156
+ # @return [Hash]
157
+ # @api public
158
+ #: () -> Hash[untyped, untyped]
159
+ def unmapped_data
160
+ Elements::EMPTY_HASH
161
+ end
162
+
163
+ # Validation errors for compatibility with Elements::Row.
164
+ #
165
+ # @return [Array<String>]
166
+ # @api public
167
+ #: () -> Array[String]
168
+ def errors
169
+ Elements::EMPTY_ERRORS
170
+ end
171
+
172
+ # Human-readable representation.
173
+ #
174
+ # @return [String]
175
+ # @api public
176
+ #: () -> String
177
+ def inspect
178
+ "#<#{self.class.name} index=#{index} height=#{height.inspect} hidden=#{hidden}>"
179
+ end
180
+ end
181
+ end
@@ -3,5 +3,5 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  module Xlsxrb
6
- VERSION = "0.1.7"
6
+ VERSION = "0.1.8"
7
7
  end
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.
@@ -360,28 +361,46 @@ module Xlsxrb
360
361
  )
361
362
  end
362
363
 
363
- # Reads an XLSX file into an in-memory Elements::Workbook.
364
+ # Reads an XLSX file (streaming / lazy-loaded by default) from a file path, IO stream, or binary String.
364
365
  #
365
- # @example Read from file path
366
- # workbook = Xlsxrb.read("data.xlsx")
367
- # sheet = workbook["Sheet1"]
368
- # puts sheet["A1"].value
366
+ # Sheets and rows are streamed lazily with O(1) constant memory. If a block is given,
367
+ # yields each StreamSheet sequentially.
369
368
  #
370
- # @example Read from IO stream
371
- # workbook = File.open("data.xlsx", "rb") { |io| Xlsxrb.read(io) }
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"]).
372
371
  #
373
- # @param source [String, IO] File path or IO object.
374
- # @return [Elements::Workbook] The parsed workbook.
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
386
+ #
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.
375
391
  # @api public
376
- #: (untyped source) -> Elements::Workbook
377
- 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
+
378
397
  attributes = source.is_a?(String) ? { "filepath" => source } : {}
379
398
  Xlsxrb.in_span("Xlsxrb.read", attributes: attributes) do
380
399
  entries = Ooxml::ZipReader.open(source, &:read_all)
381
400
  shared_strings = Ooxml::SharedStringsParser.parse(entries["xl/sharedStrings.xml"])
382
- styles = Ooxml::StylesParser.parse(entries["xl/styles.xml"])
383
401
  workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"])
384
402
  rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"])
403
+ styles = Ooxml::StylesParser.parse(entries["xl/styles.xml"])
385
404
 
386
405
  sheets = workbook_sheets.map do |sheet_info|
387
406
  target = rels[sheet_info[:r_id]]
@@ -389,24 +408,92 @@ module Xlsxrb
389
408
 
390
409
  sheet_path = target.start_with?("/") ? target.delete_prefix("/") : "xl/#{target}"
391
410
  sheet_xml = entries[sheet_path]
392
- 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
+ )
393
419
  end.compact
394
420
 
395
- 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
396
429
  end
397
430
  end
398
431
 
399
- # Writes an Elements::Workbook to an XLSX file or IO stream.
432
+ # Writes an XLSX file or IO stream (streaming or in-memory), or returns a binary string.
400
433
  #
401
- # @example Write to file
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
402
462
  # Xlsxrb.write("output.xlsx", workbook)
403
463
  #
404
- # @param target [String, IO] File path or IO object.
405
- # @param workbook [Elements::Workbook] The workbook to write.
406
- # @return [void]
407
464
  # @api public
408
- #: (untyped target, untyped workbook) -> void
409
- 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
410
497
  raise Error, "target is required" if target.nil?
411
498
  raise Error, "workbook must be an Elements::Workbook" unless workbook.is_a?(Elements::Workbook)
412
499
 
@@ -416,7 +503,8 @@ module Xlsxrb
416
503
  sst_index = {}
417
504
 
418
505
  # Collect shared strings and build index without allocating new Hashes
419
- 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
420
508
  ws.rows.each do |row|
421
509
  row.cells.each do |cell|
422
510
  val = cell.value
@@ -485,7 +573,7 @@ module Xlsxrb
485
573
  raise Error, "source is required" if source.nil?
486
574
  raise Error, "block is required" unless block_given?
487
575
 
488
- workbook = read(source)
576
+ workbook = read(source).load
489
577
  result_workbook = yield workbook
490
578
  result_workbook = workbook unless result_workbook.is_a?(Elements::Workbook)
491
579
 
@@ -494,15 +582,26 @@ module Xlsxrb
494
582
  end
495
583
 
496
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"]).
497
589
  #
498
- # @example Iterate rows in streaming mode
499
- # Xlsxrb.foreach("large_data.xlsx") do |sheet|
590
+ # @example Iterate rows and cells in streaming mode (O(1) memory)
591
+ # Xlsxrb.read("large_data.xlsx") do |sheet|
500
592
  # puts "Processing sheet: #{sheet.name}"
501
593
  # sheet.each_row do |row|
502
- # puts row.to_a.inspect
594
+ # row.each_cell do |cell|
595
+ # puts "#{cell.ref}: #{cell.value}"
596
+ # end
503
597
  # end
504
598
  # end
505
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
+ #
506
605
  # @api public
507
606
  class StreamSheet
508
607
  [Enumerable].each { |m| include m }
@@ -512,26 +611,28 @@ module Xlsxrb
512
611
  # @param name [String] The sheet name.
513
612
  # @param sheet_xml [String] Raw XML content of the sheet.
514
613
  # @param shared_strings [Array<String>] Shared strings table.
515
- #: (String name, String sheet_xml, Array[String] shared_strings) -> void
516
- def initialize(name, sheet_xml, shared_strings)
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)
517
617
  @name = name
518
618
  @sheet_xml = sheet_xml
519
619
  @shared_strings = shared_strings
620
+ @styles = styles
520
621
  end
521
622
 
522
- # Iterate over rows in this streaming sheet.
623
+ # Iterate over rows in this streaming sheet (O(1) memory).
523
624
  #
524
625
  # @yield [row]
525
- # @yieldparam row [Elements::Row]
626
+ # @yieldparam row [StreamRow, Elements::Row]
526
627
  # @return [Enumerator, void]
527
628
  # @api public
528
- #: () { (Elements::Row) -> void } -> void
529
- #: | () -> Enumerator[Elements::Row, void]
629
+ #: () { (StreamRow | Elements::Row) -> void } -> void
630
+ #: | () -> Enumerator[StreamRow | Elements::Row, void]
530
631
  def each_row
531
632
  return enum_for(:each_row) unless block_given?
532
633
 
533
634
  Ooxml::WorksheetParser.each_row(@sheet_xml, shared_strings: @shared_strings) do |row|
534
- if row.is_a?(Elements::Row)
635
+ if row.is_a?(Elements::Row) || row.is_a?(StreamRow)
535
636
  yield row
536
637
  else
537
638
  yield Xlsxrb.send(:build_row_from_raw, row)
@@ -539,88 +640,45 @@ module Xlsxrb
539
640
  end
540
641
  end
541
642
 
542
- # Iterate over rows in this streaming sheet.
643
+ # Iterate over all cells across rows continuously (O(1) memory).
543
644
  #
544
- # @yield [row]
545
- # @yieldparam row [Elements::Row]
645
+ # @yield [cell]
646
+ # @yieldparam cell [Elements::Cell]
546
647
  # @return [Enumerator, void]
547
648
  # @api public
548
- #: () { (Elements::Row) -> void } -> void
549
- #: | () -> Enumerator[Elements::Row, void]
550
- def each(&)
551
- each_row(&)
552
- end
553
- end
554
-
555
- # Streaming read: yields StreamSheet objects one at a time for each sheet in the workbook.
556
- # Keeps memory usage minimal even for multi-gigabyte XLSX files.
557
- #
558
- # @example
559
- # Xlsxrb.foreach("large.xlsx") do |sheet|
560
- # puts "Sheet: #{sheet.name}"
561
- # sheet.each_row { |row| process(row) }
562
- # end
563
- #
564
- # @param source [String, IO] File path or IO object.
565
- # @yield [sheet] Yields each sheet.
566
- # @yieldparam sheet [StreamSheet] The streaming sheet object.
567
- # @return [Enumerator, void]
568
- # @api public
569
- #: (untyped source) ?{ (StreamSheet) -> void } -> untyped
570
- def self.foreach(source)
571
- return enum_for(:foreach, source) unless block_given?
649
+ #: () { (Elements::Cell) -> void } -> void
650
+ #: | () -> Enumerator[Elements::Cell, void]
651
+ def each_cell(&)
652
+ return enum_for(:each_cell) unless block_given?
572
653
 
573
- attributes = source.is_a?(String) ? { "filepath" => source } : {}
574
- Xlsxrb.in_span("Xlsxrb.foreach", attributes: attributes) do
575
- entries = Ooxml::ZipReader.open(source, &:read_all)
576
- shared_strings = Ooxml::SharedStringsParser.parse(entries["xl/sharedStrings.xml"])
577
- workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"])
578
- rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"])
579
-
580
- workbook_sheets.each do |sheet_info|
581
- target = rels[sheet_info[:r_id]]
582
- next unless target
583
-
584
- sheet_path = target.start_with?("/") ? target.delete_prefix("/") : "xl/#{target}"
585
- sheet_xml = entries[sheet_path]
586
- next if sheet_xml.nil? || sheet_xml.empty?
587
-
588
- yield StreamSheet.new(sheet_info[:name], sheet_xml, shared_strings)
654
+ each_row do |row|
655
+ row.each_cell(&)
589
656
  end
590
657
  end
591
- end
592
658
 
593
- # Streaming write: yields a StreamWriter context for high-speed, zero-allocation XLSX generation.
594
- #
595
- # @example Generate an Excel file with styles and multiple sheets
596
- # Xlsxrb.generate("sales.xlsx") do |stream_writer|
597
- # stream_writer.sheet("Q1") do |sheet|
598
- # sheet.row(["Product", "Revenue"], styles: :bold)
599
- # sheet.row(["Widget", 15000])
600
- # end
601
- # end
602
- #
603
- # @param target [String, IO] File path or IO stream (e.g. pipe, socket, Rails response buffer).
604
- # @param strict_excel_mode [Boolean] Whether to enforce Excel specifications (max rows/cols/length).
605
- # @yield [stream_writer]
606
- # @yieldparam stream_writer [Xlsxrb::StreamWriter]
607
- # @return [void]
608
- # @api public
609
- #: (untyped target, ?strict_excel_mode: bool) ?{ (StreamWriter) -> void } -> void
610
- def self.generate(target, strict_excel_mode: true)
611
- raise Error, "target is required" if target.nil?
612
- 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
613
670
 
614
- attributes = target.is_a?(String) ? { "filepath" => target } : {}
615
- Xlsxrb.in_span("Xlsxrb.generate", attributes: attributes) do
616
- stream_writer = StreamWriter.new(target, strict_excel_mode: strict_excel_mode)
617
- begin
618
- yield stream_writer
619
- stream_writer.close
620
- ensure
621
- stream_writer.cleanup!
622
- 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)
623
680
  end
681
+ alias to_worksheet load
624
682
  end
625
683
 
626
684
  # Builds an in-memory Elements::Workbook using a declarative DSL.
@@ -0,0 +1,70 @@
1
+ # Generated from lib/xlsxrb/elements/coordinate_access.rb with RBS::Inline
2
+
3
+ module Xlsxrb
4
+ module Elements
5
+ # Mixin providing coordinate-based and random-access cell/row lookups
6
+ # for in-memory worksheet structures.
7
+ #
8
+ # Expects the including class to provide a `#rows` method returning an `Array<Elements::Row>`.
9
+ #
10
+ # @api public
11
+ module CoordinateAccess
12
+ # Returns a Hash mapping Excel cell references (e.g. "A1") to Cell objects.
13
+ #
14
+ # @return [Hash<String, Elements::Cell>]
15
+ # : () -> Hash[String, Elements::Cell]
16
+ def cells_hash: () -> Hash[String, Elements::Cell]
17
+
18
+ # Returns all cells ordered by row and column index.
19
+ #
20
+ # @return [Array<Elements::Cell>]
21
+ # @api public
22
+ # : () -> Array[Elements::Cell]
23
+ def cells: () -> Array[Elements::Cell]
24
+
25
+ # Access a cell by its Excel-style reference (e.g. "A1").
26
+ #
27
+ # @example
28
+ # sheet["A1"] #=> #<Elements::Cell value="Hello">
29
+ #
30
+ # @param ref [String, Symbol] Cell reference (e.g. "A1" or :A1).
31
+ # @return [Elements::Cell, nil]
32
+ # @api public
33
+ # : (String | Symbol ref) -> Elements::Cell?
34
+ def []: (String | Symbol ref) -> Elements::Cell?
35
+
36
+ # Returns the row at the given 0-based index, or nil.
37
+ #
38
+ # @param index [Integer] 0-based row index.
39
+ # @return [Elements::Row, nil]
40
+ # @api public
41
+ # : (Integer index) -> Elements::Row?
42
+ def row_at: (Integer index) -> Elements::Row?
43
+
44
+ # Returns the first row in the sheet, or nil.
45
+ #
46
+ # @return [Elements::Row, nil]
47
+ # @api public
48
+ # : () -> Elements::Row?
49
+ def first_row: () -> Elements::Row?
50
+
51
+ # Returns the last row in the sheet, or nil.
52
+ #
53
+ # @return [Elements::Row, nil]
54
+ # @api public
55
+ # : () -> Elements::Row?
56
+ def last_row: () -> Elements::Row?
57
+
58
+ # Returns the raw cell value at the given Excel-style reference (e.g. "A1").
59
+ #
60
+ # @example
61
+ # sheet.cell_value("A1") #=> "Sales Report"
62
+ #
63
+ # @param ref [String] Cell reference (e.g. "A1").
64
+ # @return [Object, nil]
65
+ # @api public
66
+ # : (String ref) -> untyped
67
+ def cell_value: (String ref) -> untyped
68
+ end
69
+ end
70
+ end