xlsxrb 0.1.12 → 0.1.13

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.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -42
  3. data/README.md +26 -22
  4. data/lib/xlsxrb/dsl_helpers.rb +59 -0
  5. data/lib/xlsxrb/elements/workbook.rb +11 -0
  6. data/lib/xlsxrb/ooxml/shared_strings_parser.rb +3 -5
  7. data/lib/xlsxrb/ooxml/workbook_writer.rb +5 -7
  8. data/lib/xlsxrb/ooxml/worksheet_parser.rb +172 -9
  9. data/lib/xlsxrb/ooxml/writer.rb +2 -1
  10. data/lib/xlsxrb/ooxml/xml_builder.rb +20 -0
  11. data/lib/xlsxrb/ooxml/zip_reader.rb +364 -78
  12. data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
  13. data/lib/xlsxrb/stream_sheet.rb +94 -6
  14. data/lib/xlsxrb/stream_writer.rb +33 -66
  15. data/lib/xlsxrb/version.rb +1 -1
  16. data/lib/xlsxrb/workbook_builder.rb +1 -1
  17. data/lib/xlsxrb/worksheet_builder.rb +3 -31
  18. data/lib/xlsxrb.rb +448 -171
  19. data/sig/generated/xlsxrb/dsl_helpers.rbs +32 -0
  20. data/sig/generated/xlsxrb/elements/workbook.rbs +7 -0
  21. data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +0 -2
  22. data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +5 -4
  23. data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +11 -0
  24. data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +96 -8
  25. data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
  26. data/sig/generated/xlsxrb/stream_sheet.rbs +43 -3
  27. data/sig/generated/xlsxrb.rbs +18 -11
  28. metadata +6 -146
  29. data/.devcontainer/Dockerfile +0 -65
  30. data/.devcontainer/devcontainer.json +0 -17
  31. data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
  32. data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
  33. data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
  34. data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
  35. data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
  36. data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
  37. data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
  38. data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
  39. data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
  40. data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
  41. data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
  42. data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
  43. data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
  44. data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
  45. data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
  46. data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
  47. data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
  48. data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
  49. data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
  50. data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
  51. data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
  52. data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
  53. data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
  54. data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
  55. data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
  56. data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
  57. data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
  58. data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
  59. data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
  60. data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
  61. data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
  62. data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
  63. data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
  64. data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
  65. data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
  66. data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
  67. data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
  68. data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
  69. data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
  70. data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
  71. data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
  72. data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
  73. data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
  74. data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
  75. data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
  76. data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
  77. data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
  78. data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
  79. data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
  80. data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
  81. data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
  82. data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
  83. data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
  84. data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
  85. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
  86. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
  87. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
  88. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
  89. data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
  90. data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
  91. data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
  92. data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
  93. data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
  94. data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
  95. data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
  96. data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
  97. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
  98. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
  99. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
  100. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
  101. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
  102. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
  103. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
  104. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
  105. data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
  106. data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
  107. data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
  108. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
  109. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
  110. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
  111. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
  112. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
  113. data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
  114. data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
  115. data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
  116. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
  117. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
  118. data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
  119. data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
  120. data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
  121. data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
  122. data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
  123. data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
  124. data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
  125. data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
  126. data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
  127. data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
  128. data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
  129. data/.mutant.yml +0 -8
  130. data/Rakefile +0 -633
  131. data/Steepfile +0 -18
  132. data/benchmark.rb +0 -406
  133. data/docs/ARCHITECTURE.md +0 -514
  134. data/docs/DEVELOPMENT.md +0 -137
  135. data/docs/MUTATION_TESTING.md +0 -52
  136. data/docs/PEER_LIBRARIES.md +0 -121
  137. data/docs/QUALITY_ASSURANCE.md +0 -26
  138. data/docs/SPEC_SOURCES.md +0 -48
  139. data/docs/assets/benchmark_results.svg +0 -144
  140. data/docs/assets/lsp_autocompletion.png +0 -0
  141. data/docs/assets/playground_preview.png +0 -0
  142. data/docs/coi-serviceworker.js +0 -82
  143. data/docs/office_thread.js +0 -77
  144. data/docs/preview.html +0 -719
  145. data/docs/visual/VisualGallery.md +0 -4683
  146. data/docs/wasm/ruby.wasm +0 -0
  147. data/docs/wasm/wasm_doc_helper.css +0 -351
  148. data/docs/wasm/wasm_doc_helper.js +0 -439
  149. data/docs/zeta.js +0 -1107
  150. data/rbs_collection.lock.yaml +0 -252
  151. data/rbs_collection.yaml +0 -19
  152. data/vendor/sdk_runner/Program.cs +0 -93
  153. data/vendor/sdk_runner/sdk_runner.csproj +0 -13
@@ -36,15 +36,20 @@ module Xlsxrb
36
36
  # Initializes a streaming worksheet context.
37
37
  #
38
38
  # @param name [String] The sheet name.
39
- # @param sheet_xml [String] Raw XML content of the worksheet.
39
+ # @param sheet_source [String, Proc, IO, nil] Raw XML content or chunk supplier/stream.
40
40
  # @param shared_strings [Array<String>] Shared strings table.
41
41
  # @param styles [Hash, nil] Optional parsed styles hash.
42
- #: (String name, String sheet_xml, Array[String] shared_strings, ?Hash[untyped, untyped]? styles) -> void
43
- def initialize(name, sheet_xml, shared_strings, styles = nil)
42
+ # @param zip_reader [Ooxml::ZipReader, nil] Optional ZipReader context.
43
+ # @param entry_name [String, nil] Archive entry name for this sheet.
44
+ #: (String name, untyped sheet_source, Array[String] shared_strings, ?Hash[untyped, untyped]? styles, ?zip_reader: Ooxml::ZipReader?, ?entry_name: String?) -> void
45
+ def initialize(name, sheet_source, shared_strings, styles = nil, zip_reader: nil, entry_name: nil)
44
46
  @name = name
45
- @sheet_xml = sheet_xml
47
+ @sheet_source = sheet_source
46
48
  @shared_strings = shared_strings
47
49
  @styles = styles
50
+ @zip_reader = zip_reader
51
+ @entry_name = entry_name
52
+ @sheet_xml = sheet_source if sheet_source.is_a?(String)
48
53
  end
49
54
 
50
55
  # Iterates over rows in this streaming worksheet with O(1) memory.
@@ -63,7 +68,13 @@ module Xlsxrb
63
68
  def each_row(&)
64
69
  return enum_for(:each_row) unless block_given?
65
70
 
66
- Ooxml::WorksheetParser.each_row(@sheet_xml, shared_strings: @shared_strings, &)
71
+ source = if @sheet_source
72
+ @sheet_source
73
+ elsif @zip_reader && @entry_name
74
+ ->(&blk) { @zip_reader.each_entry_chunk(@entry_name, &blk) }
75
+ end
76
+
77
+ Ooxml::WorksheetParser.each_row(source, shared_strings: @shared_strings, &)
67
78
  end
68
79
 
69
80
  # Iterates over all cells across all rows continuously with O(1) memory.
@@ -112,8 +123,85 @@ module Xlsxrb
112
123
  # @api public
113
124
  #: () -> Elements::Worksheet
114
125
  def load
115
- Xlsxrb.send(:build_worksheet, @name, @sheet_xml, @shared_strings, @styles)
126
+ Xlsxrb.send(:build_worksheet, @name, raw_sheet_xml, @shared_strings, @styles)
116
127
  end
117
128
  alias to_worksheet load
129
+
130
+ # Returns merged cell ranges (e.g. ["A1:B2"]) for this worksheet.
131
+ #
132
+ # @return [Array<String>]
133
+ # @api public
134
+ #: () -> Array[String]
135
+ def merged_cells
136
+ @merged_cells ||= begin
137
+ listener = Ooxml::Reader::MergeCellsListener.new
138
+ Ooxml::XmlParser.parse(raw_sheet_xml, listener)
139
+ listener.ranges
140
+ end
141
+ end
142
+
143
+ # Returns the auto-filter range (e.g. "A1:E100") for this worksheet, or nil if none.
144
+ #
145
+ # @return [String, nil]
146
+ # @api public
147
+ #: () -> String?
148
+ def auto_filter
149
+ @auto_filter ||= begin
150
+ listener = Ooxml::Reader::AutoFilterListener.new
151
+ Ooxml::XmlParser.parse(raw_sheet_xml, listener)
152
+ listener.ref
153
+ end
154
+ end
155
+
156
+ # Returns data validation rules configured for this worksheet.
157
+ #
158
+ # @return [Array<Hash[Symbol, untyped]>]
159
+ # @api public
160
+ #: () -> Array[Hash[Symbol, untyped]]
161
+ def data_validations
162
+ @data_validations ||= begin
163
+ listener = Ooxml::Reader::DataValidationsListener.new
164
+ Ooxml::XmlParser.parse(raw_sheet_xml, listener)
165
+ listener.validations
166
+ end
167
+ end
168
+
169
+ # Returns conditional formatting rules configured for this worksheet.
170
+ #
171
+ # @return [Array<Hash[Symbol, untyped]>]
172
+ # @api public
173
+ #: () -> Array[Hash[Symbol, untyped]]
174
+ def conditional_formats
175
+ @conditional_formats ||= begin
176
+ listener = Ooxml::Reader::ConditionalFormattingListener.new
177
+ Ooxml::XmlParser.parse(raw_sheet_xml, listener)
178
+ listener.rules
179
+ end
180
+ end
181
+
182
+ # Closes any underlying reader resources.
183
+ #: () -> void
184
+ def close
185
+ @zip_reader&.close
186
+ nil
187
+ end
188
+
189
+ private
190
+
191
+ # Returns the full raw XML string for this worksheet, loaded on demand.
192
+ #: () -> String
193
+ def raw_sheet_xml
194
+ @raw_sheet_xml ||= if @sheet_source.is_a?(String)
195
+ @sheet_source
196
+ elsif @zip_reader && @entry_name
197
+ @zip_reader.read_entry(@entry_name) || ""
198
+ elsif @sheet_source.respond_to?(:call)
199
+ buf = +""
200
+ @sheet_source.call { |c| buf << c }
201
+ buf
202
+ else
203
+ ""
204
+ end
205
+ end
118
206
  end
119
207
  end
@@ -911,39 +911,11 @@ module Xlsxrb
911
911
  sheet if @current_sheet.nil?
912
912
 
913
913
  row_index = @current_row_index
914
- # See: https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
915
- if @strict_excel_mode
916
- raise ArgumentError, "Row index #{row_index} exceeds Excel limit of 1,048,576 rows" if row_index >= 1_048_576
917
- raise ArgumentError, "Row height #{height} must be between 0 and 409 points (Excel limitation)" if height && (height.negative? || height > 409)
918
- end
914
+ DslHelpers.validate_row_bounds!(row_index, height, strict_excel_mode: @strict_excel_mode)
919
915
  @current_row_index += 1
920
916
 
921
- if values.is_a?(Hash)
922
- max_col = values.keys.map { |k| Elements::Cell.column_index(k) }.max || -1
923
- cells_array = Array.new(max_col + 1)
924
- values.each do |k, v|
925
- idx = Elements::Cell.column_index(k)
926
- cells_array[idx] = v
927
- end
928
- values = cells_array
929
- end
930
-
931
- if styles.is_a?(Hash)
932
- expanded_styles = {}
933
- styles.each do |k, v|
934
- if k.is_a?(Range) || k.is_a?(Array)
935
- k.each { |idx| expanded_styles[Elements::Cell.column_index(idx)] = v }
936
- else
937
- expanded_styles[Elements::Cell.column_index(k)] = v
938
- end
939
- end
940
- max_col_style = expanded_styles.keys.max || -1
941
- styles_array = Array.new(max_col_style + 1)
942
- expanded_styles.each do |idx, v|
943
- styles_array[idx] = v
944
- end
945
- styles = styles_array
946
- end
917
+ values = DslHelpers.normalize_row_values(values)
918
+ styles = DslHelpers.normalize_row_styles(styles)
947
919
 
948
920
  if @current_charts && !@current_charts.empty?
949
921
  @current_cells ||= {}
@@ -1509,37 +1481,35 @@ module Xlsxrb
1509
1481
  def close
1510
1482
  raise ArgumentError, "Workbook must contain at least one sheet (Excel limitation)" if @strict_excel_mode && @sheets.empty? && @current_sheet.nil?
1511
1483
 
1512
- Xlsxrb.in_span("StreamWriter#close") do
1513
- flush_current_sheet
1514
-
1515
- styles_definition = {
1516
- fonts: @style_writer.fonts.dup,
1517
- fills: @style_writer.fills.dup,
1518
- borders: @style_writer.borders.dup,
1519
- xf_entries: @style_writer.xf_entries.dup,
1520
- num_fmts: @style_writer.num_fmts.dup,
1521
- dxfs: @dxfs || []
1522
- }
1523
-
1524
- resolved_names = resolve_defined_names(@defined_names, @sheets)
1525
-
1526
- wb_writer = Ooxml::WorkbookWriter.new(
1527
- sheets: @sheets,
1528
- shared_strings: @sst,
1529
- shared_strings_index: @sst_index,
1530
- styles: styles_definition,
1531
- defined_names: resolved_names.empty? ? nil : resolved_names,
1532
- core_properties: @core_properties.empty? ? nil : @core_properties,
1533
- app_properties: @app_properties.empty? ? nil : @app_properties,
1534
- custom_properties: @custom_properties.empty? ? nil : @custom_properties,
1535
- workbook_protection: @workbook_protection,
1536
- workbook_properties: @workbook_properties
1537
- )
1538
- wb_writer.write_package_parts(@zip)
1539
-
1540
- @zip.close
1541
- @io.close if @owns_io && !@io.closed?
1542
- end
1484
+ flush_current_sheet
1485
+
1486
+ styles_definition = {
1487
+ fonts: @style_writer.fonts.dup,
1488
+ fills: @style_writer.fills.dup,
1489
+ borders: @style_writer.borders.dup,
1490
+ xf_entries: @style_writer.xf_entries.dup,
1491
+ num_fmts: @style_writer.num_fmts.dup,
1492
+ dxfs: @dxfs || []
1493
+ }
1494
+
1495
+ resolved_names = resolve_defined_names(@defined_names, @sheets)
1496
+
1497
+ wb_writer = Ooxml::WorkbookWriter.new(
1498
+ sheets: @sheets,
1499
+ shared_strings: @sst,
1500
+ shared_strings_index: @sst_index,
1501
+ styles: styles_definition,
1502
+ defined_names: resolved_names.empty? ? nil : resolved_names,
1503
+ core_properties: @core_properties.empty? ? nil : @core_properties,
1504
+ app_properties: @app_properties.empty? ? nil : @app_properties,
1505
+ custom_properties: @custom_properties.empty? ? nil : @custom_properties,
1506
+ workbook_protection: @workbook_protection,
1507
+ workbook_properties: @workbook_properties
1508
+ )
1509
+ wb_writer.write_package_parts(@zip)
1510
+
1511
+ @zip.close
1512
+ @io.close if @owns_io && !@io.closed?
1543
1513
  ensure
1544
1514
  cleanup!
1545
1515
  end
@@ -1570,10 +1540,7 @@ module Xlsxrb
1570
1540
 
1571
1541
  #: (untyped range) -> untyped
1572
1542
  def absolute_range(range)
1573
- # simplecov:disable
1574
- # Edge case / untested delegation block
1575
- range.gsub(/([A-Z]+)(\d+)/, '$\1$\2')
1576
- # simplecov:enable
1543
+ DslHelpers.absolute_range(range)
1577
1544
  end
1578
1545
 
1579
1546
  #: (untyped names, untyped sheets) -> untyped
@@ -3,5 +3,5 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  module Xlsxrb
6
- VERSION = "0.1.12"
6
+ VERSION = "0.1.13"
7
7
  end
@@ -210,7 +210,7 @@ module Xlsxrb
210
210
 
211
211
  #: (untyped range) -> untyped
212
212
  def absolute_range(range)
213
- range.gsub(/([A-Z]+)(\d+)/, '$\1$\2')
213
+ DslHelpers.absolute_range(range)
214
214
  end
215
215
 
216
216
  #: (untyped names, untyped sheets) -> untyped
@@ -87,38 +87,10 @@ module Xlsxrb
87
87
  #: (Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
88
88
  def row(values, styles: nil, height: nil, hidden: false, custom_height: false, outline_level: nil)
89
89
  row_index = @rows.size
90
- # See: https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
91
- if @strict_excel_mode
92
- raise ArgumentError, "Row index #{row_index} exceeds Excel limit of 1,048,576 rows" if row_index >= 1_048_576
93
- raise ArgumentError, "Row height #{height} must be between 0 and 409 points (Excel limitation)" if height && (height.negative? || height > 409)
94
- end
95
-
96
- if values.is_a?(Hash)
97
- max_col = values.keys.map { |k| Elements::Cell.column_index(k) }.max || -1
98
- cells_array = Array.new(max_col + 1)
99
- values.each do |k, v|
100
- idx = Elements::Cell.column_index(k)
101
- cells_array[idx] = v
102
- end
103
- values = cells_array
104
- end
90
+ DslHelpers.validate_row_bounds!(row_index, height, strict_excel_mode: @strict_excel_mode)
105
91
 
106
- if styles.is_a?(Hash)
107
- expanded_styles = {}
108
- styles.each do |k, v|
109
- if k.is_a?(Range) || k.is_a?(Array)
110
- k.each { |idx| expanded_styles[Elements::Cell.column_index(idx)] = v }
111
- else
112
- expanded_styles[Elements::Cell.column_index(k)] = v
113
- end
114
- end
115
- max_col_style = expanded_styles.keys.max || -1
116
- styles_array = Array.new(max_col_style + 1)
117
- expanded_styles.each do |idx, v|
118
- styles_array[idx] = v
119
- end
120
- styles = styles_array
121
- end
92
+ values = DslHelpers.normalize_row_values(values)
93
+ styles = DslHelpers.normalize_row_styles(styles)
122
94
 
123
95
  # Auto-detect Date / Time for built-in styles
124
96
  values.each_with_index do |val, idx|