Module: Xlsxrb
- Defined in:
- lib/xlsxrb.rb,
lib/xlsxrb/ooxml.rb,
lib/xlsxrb/version.rb,
lib/xlsxrb/elements.rb,
lib/xlsxrb/ooxml/utils.rb,
lib/xlsxrb/elements/row.rb,
lib/xlsxrb/ooxml/reader.rb,
lib/xlsxrb/ooxml/writer.rb,
lib/xlsxrb/elements/cell.rb,
lib/xlsxrb/style_builder.rb,
lib/xlsxrb/elements/types.rb,
lib/xlsxrb/elements/column.rb,
lib/xlsxrb/ooxml/xml_parser.rb,
lib/xlsxrb/ooxml/zip_reader.rb,
lib/xlsxrb/ooxml/zip_writer.rb,
lib/xlsxrb/elements/workbook.rb,
lib/xlsxrb/ooxml/xml_builder.rb,
lib/xlsxrb/elements/worksheet.rb,
lib/xlsxrb/ooxml/styles_parser.rb,
lib/xlsxrb/ooxml/zip_generator.rb,
lib/xlsxrb/ooxml/workbook_parser.rb,
lib/xlsxrb/ooxml/workbook_writer.rb,
lib/xlsxrb/ooxml/worksheet_parser.rb,
lib/xlsxrb/ooxml/worksheet_writer.rb,
lib/xlsxrb/ooxml/shared_strings_parser.rb,
sig/generated/xlsxrb.rbs,
sig/generated/xlsxrb/ooxml.rbs,
sig/generated/xlsxrb/version.rbs,
sig/generated/xlsxrb/elements.rbs,
sig/generated/xlsxrb/ooxml/utils.rbs,
sig/generated/xlsxrb/elements/row.rbs,
sig/generated/xlsxrb/ooxml/reader.rbs,
sig/generated/xlsxrb/ooxml/writer.rbs,
sig/generated/xlsxrb/elements/cell.rbs,
sig/generated/xlsxrb/style_builder.rbs,
sig/generated/xlsxrb/elements/types.rbs,
sig/generated/xlsxrb/elements/column.rbs,
sig/generated/xlsxrb/ooxml/xml_parser.rbs,
sig/generated/xlsxrb/ooxml/zip_reader.rbs,
sig/generated/xlsxrb/ooxml/zip_writer.rbs,
sig/generated/xlsxrb/elements/workbook.rbs,
sig/generated/xlsxrb/ooxml/xml_builder.rbs,
sig/generated/xlsxrb/elements/worksheet.rbs,
sig/generated/xlsxrb/ooxml/styles_parser.rbs,
sig/generated/xlsxrb/ooxml/zip_generator.rbs,
sig/generated/xlsxrb/ooxml/workbook_parser.rbs,
sig/generated/xlsxrb/ooxml/workbook_writer.rbs,
sig/generated/xlsxrb/ooxml/worksheet_parser.rbs,
sig/generated/xlsxrb/ooxml/worksheet_writer.rbs,
sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs
Overview
Ruby XLSX read/write library.
Defined Under Namespace
Modules: Elements, Ooxml Classes: ChartBuilder, Error, ParseError, StreamSheet, StreamWriter, StyleBuilder, ValidationError, WorkbookBuilder, WorksheetBuilder, ZipError
Constant Summary collapse
- TRACER =
OpenTelemetry.tracer_provider.tracer("xlsxrb", Xlsxrb::VERSION)
- VERSION =
"0.1.7"
Class Method Summary collapse
-
.build(strict_excel_mode: true) {|builder| ... } ⇒ Elements::Workbook
Builds an in-memory Elements::Workbook using a declarative DSL.
-
.build_raw_cell_from_value(row_index, col_index, value, sst, sst_index) ⇒ Object
Builds a raw cell hash from a value for streaming writes.
-
.foreach(source) {|sheet| ... } ⇒ Enumerator, void
Streaming read: yields StreamSheet objects one at a time for each sheet in the workbook.
-
.formula(expression, cached_value: nil) ⇒ Elements::Formula
Creates a Formula object for use in row values.
-
.generate(target, strict_excel_mode: true) {|stream_writer| ... } ⇒ void
Streaming write: yields a StreamWriter context for high-speed, zero-allocation XLSX generation.
- .in_span(name, attributes: nil) ⇒ void
-
.modify(source, target = nil) {|workbook| ... } ⇒ void
Modifies an existing XLSX file.
-
.read(source) ⇒ Elements::Workbook
Reads an XLSX file into an in-memory Elements::Workbook.
-
.rich_text(*runs, text: nil, **font_props) ⇒ Elements::RichText
Helper to easily create RichText objects.
-
.write(target, workbook) ⇒ void
Writes an Elements::Workbook to an XLSX file or IO stream.
Class Method Details
.build(strict_excel_mode: true) {|builder| ... } ⇒ Elements::Workbook
Builds an in-memory Elements::Workbook using a declarative DSL.
: (?strict_excel_mode: bool) ?{ (WorkbookBuilder) -> void } -> Elements::Workbook
642 643 644 645 646 647 648 649 650 |
# File 'lib/xlsxrb.rb', line 642 def self.build(strict_excel_mode: true) raise Error, "block is required" unless block_given? Xlsxrb.in_span("Xlsxrb.build") do builder = WorkbookBuilder.new(strict_excel_mode: strict_excel_mode) yield builder builder.build end end |
.build_raw_cell_from_value(row_index, col_index, value, sst, sst_index) ⇒ Object
Builds a raw cell hash from a value for streaming writes. : (untyped row_index, untyped col_index, untyped value, untyped sst, untyped sst_index) -> untyped
3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 |
# File 'lib/xlsxrb.rb', line 3225 def self.build_raw_cell_from_value(row_index, col_index, value, sst, sst_index) # simplecov:disable # Edge case / untested delegation block ref = "#{Elements::Cell.column_letter(col_index)}#{row_index + 1}" result = { ref: ref } case value when Elements::Formula result[:formula] = value.expression result[:formula_ca] = true if value.calculate_always result[:value] = value.cached_value if value.cached_value when String idx = sst_index[value] ||= begin sst << value sst.size - 1 end result[:value] = idx result[:type] = "s" when true, false result[:value] = value result[:type] = "b" when Integer, Float result[:value] = value when Date result[:value] = Xlsxrb::Ooxml::Utils.date_to_serial(value) when Time result[:value] = Xlsxrb::Ooxml::Utils.datetime_to_serial(value) # simplecov:enable when NilClass # empty cell end # simplecov:disable # Edge case / untested delegation block result # simplecov:enable end |
.foreach(source) {|sheet| ... } ⇒ Enumerator, void
Streaming read: yields StreamSheet objects one at a time for each sheet in the workbook. Keeps memory usage minimal even for multi-gigabyte XLSX files.
: (untyped source) ?{ (StreamSheet) -> void } -> untyped
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 |
# File 'lib/xlsxrb.rb', line 570 def self.foreach(source) return enum_for(:foreach, source) unless block_given? attributes = source.is_a?(String) ? { "filepath" => source } : {} Xlsxrb.in_span("Xlsxrb.foreach", attributes: attributes) do entries = Ooxml::ZipReader.open(source, &:read_all) shared_strings = Ooxml::SharedStringsParser.parse(entries["xl/sharedStrings.xml"]) workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"]) rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"]) workbook_sheets.each do |sheet_info| target = rels[sheet_info[:r_id]] next unless target sheet_path = target.start_with?("/") ? target.delete_prefix("/") : "xl/#{target}" sheet_xml = entries[sheet_path] next if sheet_xml.nil? || sheet_xml.empty? yield StreamSheet.new(sheet_info[:name], sheet_xml, shared_strings) end end end |
.formula(expression, cached_value: nil) ⇒ Elements::Formula
Creates a Formula object for use in row values.
: (String expression, ?cached_value: String | Numeric | bool | nil) -> Elements::Formula
355 356 357 358 359 360 361 |
# File 'lib/xlsxrb.rb', line 355 def self.formula(expression, cached_value: nil) Elements::Formula.new( expression: expression, cached_value: cached_value, calculate_always: cached_value.nil? || nil ) end |
.generate(target, strict_excel_mode: true) {|stream_writer| ... } ⇒ void
This method returns an undefined value.
Streaming write: yields a StreamWriter context for high-speed, zero-allocation XLSX generation.
: (untyped target, ?strict_excel_mode: bool) ?{ (StreamWriter) -> void } -> void
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
# File 'lib/xlsxrb.rb', line 610 def self.generate(target, strict_excel_mode: true) raise Error, "target is required" if target.nil? raise Error, "block is required" unless block_given? attributes = target.is_a?(String) ? { "filepath" => target } : {} Xlsxrb.in_span("Xlsxrb.generate", attributes: attributes) do stream_writer = StreamWriter.new(target, strict_excel_mode: strict_excel_mode) begin yield stream_writer stream_writer.close ensure stream_writer.cleanup! end end end |
.in_span(name, attributes: nil) ⇒ void
This method returns an undefined value.
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/xlsxrb.rb', line 47 def self.in_span(name, attributes: nil, &) if defined?(Ractor) && Ractor.current != Ractor.main # simplecov:disable # Test suite runs in the main Ractor. This branch is for multi-threaded usage via Ractors. yield # simplecov:enable elsif attributes TRACER.in_span(name, attributes: attributes, &) else TRACER.in_span(name, &) end end |
.modify(source, target = nil) {|workbook| ... } ⇒ void
This method returns an undefined value.
Modifies an existing XLSX file.
Reads the workbook, passes it to the block, and writes the result.
The block receives an Elements::Workbook and must return a modified one (e.g. via update_sheet).
If no target is given, the source is overwritten.
: (untyped source, ?untyped target) ?{ (Elements::Workbook) -> untyped } -> void
484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/xlsxrb.rb', line 484 def self.modify(source, target = nil) raise Error, "source is required" if source.nil? raise Error, "block is required" unless block_given? workbook = read(source) result_workbook = yield workbook result_workbook = workbook unless result_workbook.is_a?(Elements::Workbook) write_target = target || source write(write_target, result_workbook) end |
.read(source) ⇒ Elements::Workbook
Reads an XLSX file into an in-memory Elements::Workbook.
: (untyped source) -> Elements::Workbook
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/xlsxrb.rb', line 377 def self.read(source) attributes = source.is_a?(String) ? { "filepath" => source } : {} Xlsxrb.in_span("Xlsxrb.read", attributes: attributes) do entries = Ooxml::ZipReader.open(source, &:read_all) shared_strings = Ooxml::SharedStringsParser.parse(entries["xl/sharedStrings.xml"]) styles = Ooxml::StylesParser.parse(entries["xl/styles.xml"]) workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"]) rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"]) sheets = workbook_sheets.map do |sheet_info| target = rels[sheet_info[:r_id]] next nil unless target sheet_path = target.start_with?("/") ? target.delete_prefix("/") : "xl/#{target}" sheet_xml = entries[sheet_path] build_worksheet(sheet_info[:name], sheet_xml, shared_strings, styles) end.compact Elements::Workbook.new(sheets: sheets, shared_strings: shared_strings, styles: styles) end end |
.rich_text(*runs, text: nil, **font_props) ⇒ Elements::RichText
Helper to easily create RichText objects.
Supports both Xlsxrb.rich_text({ text: "A" }, { text: "B" })
and Xlsxrb.rich_text(text: "Hi", bold: true)
: (*untyped runs, ?text: String?, **untyped font_props) -> untyped
70 71 72 73 |
# File 'lib/xlsxrb.rb', line 70 def self.rich_text(*runs, text: nil, **font_props) runs = [{ text: text, font: font_props }] if text Elements::RichText.new(runs: runs) end |
.write(target, workbook) ⇒ void
This method returns an undefined value.
Writes an Elements::Workbook to an XLSX file or IO stream.
: (untyped target, untyped workbook) -> void
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/xlsxrb.rb', line 409 def self.write(target, workbook) raise Error, "target is required" if target.nil? raise Error, "workbook must be an Elements::Workbook" unless workbook.is_a?(Elements::Workbook) attributes = target.is_a?(String) ? { "filepath" => target } : {} Xlsxrb.in_span("Xlsxrb.write", attributes: attributes) do sst = [] sst_index = {} # Collect shared strings and build index without allocating new Hashes sheet_data = workbook.sheets.map do |ws| ws.rows.each do |row| row.cells.each do |cell| val = cell.value if (val.is_a?(String) || val.is_a?(Elements::RichText)) && !sst_index.key?(val) sst << val sst_index[val] = sst.size - 1 end end end columns = ws.columns.map do |col| # simplecov:disable # Edge case / untested delegation block { index: col.index, width: col.width, hidden: col.hidden, custom_width: col.custom_width, outline_level: col.outline_level } # simplecov:enable end sd = { name: ws.name, rows: ws.rows, columns: columns } sd[:charts] = ws.charts unless ws.charts.empty? # Extract facade metadata from unmapped_data facade = ws.unmapped_data[:facade] facade&.each { |key, val| sd[key] = val } sd end # Extract workbook-level facade metadata wb_facade = workbook.unmapped_data[:facade] || {} Ooxml::WorkbookWriter.write( target, sheets: sheet_data, shared_strings: sst, shared_strings_index: sst_index, styles: workbook.styles, defined_names: wb_facade[:defined_names], core_properties: wb_facade[:core_properties], app_properties: wb_facade[:app_properties], custom_properties: wb_facade[:custom_properties], workbook_protection: wb_facade[:workbook_protection], workbook_properties: wb_facade[:workbook_properties] ) end end |