Module: Xlsxrb
- Defined in:
- lib/xlsxrb.rb,
lib/xlsxrb/ooxml.rb,
lib/xlsxrb/version.rb,
lib/xlsxrb/elements.rb,
lib/xlsxrb/stream_row.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/elements/coordinate_access.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/stream_row.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/elements/coordinate_access.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, StreamRow, StreamSheet, StreamWriter, StyleBuilder, ValidationError, WorkbookBuilder, WorksheetBuilder, ZipError
Constant Summary collapse
- TRACER =
OpenTelemetry.tracer_provider.tracer("xlsxrb", Xlsxrb::VERSION)
- VERSION =
"0.1.8"
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.
-
.formula(expression, cached_value: nil) ⇒ Elements::Formula
Creates a Formula object for use in row values.
- .in_span(name, attributes: nil) ⇒ void
-
.modify(source, target = nil) {|workbook| ... } ⇒ void
Modifies an existing XLSX file.
-
.read(source) {|sheet| ... } ⇒ Elements::Workbook, void
Reads an XLSX file (streaming / lazy-loaded by default) from a file path, IO stream, or binary String.
-
.rich_text(*runs, text: nil, **font_props) ⇒ Elements::RichText
Helper to easily create RichText objects.
-
.write(target_or_workbook, workbook_or_nil = nil, strict_excel_mode: true, &block) ⇒ Object
Writes an XLSX file or IO stream (streaming or in-memory), or returns a binary string.
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
700 701 702 703 704 705 706 707 708 |
# File 'lib/xlsxrb.rb', line 700 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
3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 |
# File 'lib/xlsxrb.rb', line 3283 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 |
.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
356 357 358 359 360 361 362 |
# File 'lib/xlsxrb.rb', line 356 def self.formula(expression, cached_value: nil) Elements::Formula.new( expression: expression, cached_value: cached_value, calculate_always: cached_value.nil? || nil ) end |
.in_span(name, attributes: nil) ⇒ void
This method returns an undefined value.
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/xlsxrb.rb', line 48 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
572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/xlsxrb.rb', line 572 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).load 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) ⇒ void .read(source) ⇒ Elements::Workbook
Reads an XLSX file (streaming / lazy-loaded by default) from a file path, IO stream, or binary String.
Sheets and rows are streamed lazily with O(1) constant memory. If a block is given, yields each StreamSheet sequentially.
Call #load on the returned Workbook or Sheet to convert to an in-memory representation for coordinate random access (e.g. sheet).
: (String | IO source) { (StreamSheet) -> void } -> void : (String | IO source) -> Elements::Workbook
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 |
# File 'lib/xlsxrb.rb', line 394 def self.read(source, &) source = StringIO.new(source) if source.is_a?(String) && (source.start_with?("PK\x03\x04") || source.include?("\x00")) 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"]) workbook_sheets = Ooxml::WorkbookParser.parse(entries["xl/workbook.xml"]) rels = Ooxml::RelationshipsParser.parse(entries["xl/_rels/workbook.xml.rels"]) styles = Ooxml::StylesParser.parse(entries["xl/styles.xml"]) 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] next nil if sheet_xml.nil? || sheet_xml.empty? StreamSheet.new( sheet_info[:name], sheet_xml, shared_strings, styles ) end.compact wb = Elements::Workbook.new(sheets: sheets, shared_strings: shared_strings, styles: styles) if block_given? sheets.each(&) nil else wb end 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
71 72 73 74 |
# File 'lib/xlsxrb.rb', line 71 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, strict_excel_mode: true) {|stream_writer| ... } ⇒ void .write(workbook) ⇒ String .write(target, workbook) ⇒ void .write(workbook) ⇒ String .write(target, workbook) ⇒ void .write(target, strict_excel_mode:) ⇒ void
Writes an XLSX file or IO stream (streaming or in-memory), or returns a binary string.
: (Elements::Workbook workbook) -> String : (String | IO target, Elements::Workbook workbook) -> void : (String | IO target, ?strict_excel_mode: bool) ?{ (StreamWriter) -> void } -> void
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 |
# File 'lib/xlsxrb.rb', line 468 def self.write(target_or_workbook, workbook_or_nil = nil, strict_excel_mode: true, &block) if block_given? target = target_or_workbook raise Error, "target is required" if target.nil? attributes = target.is_a?(String) ? { "filepath" => target } : {} return Xlsxrb.in_span("Xlsxrb.write", 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 if workbook_or_nil.nil? wb = target_or_workbook raise Error, "workbook must be an Elements::Workbook" unless wb.is_a?(Elements::Workbook) io = StringIO.new io.binmode write(io, wb) return io.string.b end target = target_or_workbook workbook = workbook_or_nil 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 |raw_ws| ws = raw_ws.respond_to?(:load) ? raw_ws.load : raw_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 |