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.6"
Class Method Summary collapse
-
.build(strict_excel_mode: true) {|builder| ... } ⇒ Elements::Workbook
Builds an Elements::Workbook in memory using a 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.
-
.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 building XLSX on-the-fly.
- .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 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.
Class Method Details
.build(strict_excel_mode: true) {|builder| ... } ⇒ Elements::Workbook
Builds an Elements::Workbook in memory using a DSL.
: (?strict_excel_mode: bool) ?{ (WorkbookBuilder) -> void } -> untyped
575 576 577 578 579 580 581 582 583 |
# File 'lib/xlsxrb.rb', line 575 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
2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 |
# File 'lib/xlsxrb.rb', line 2876 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.
: (untyped source) ?{ (StreamSheet) -> void } -> untyped
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/xlsxrb.rb', line 521 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) -> untyped
349 350 351 352 353 354 355 |
# File 'lib/xlsxrb.rb', line 349 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 building XLSX on-the-fly.
: (untyped target, ?strict_excel_mode: bool) ?{ (Xlsxrb::StreamWriter) -> void } -> void
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/xlsxrb.rb', line 552 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) ?{ (untyped) -> untyped } -> void
467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/xlsxrb.rb', line 467 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 Elements::Workbook.
: (untyped source) -> untyped
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/xlsxrb.rb', line 363 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.
: (untyped target, untyped workbook) -> void
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 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 |
# File 'lib/xlsxrb.rb', line 392 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 |