class Xlsxrb::StreamWriter
High-performance streaming writer that outputs XLSX files with O(1) constant memory.
@example Streaming write using StreamWriter
Xlsxrb.write("output.xlsx") do |writer| writer.sheet("Sales") do |s| s.row(["Item", "Price"]) s.row(["Coffee", 3.50]) end end
@api public
Attributes
String?
@return [String, nil] Name of the currently active worksheet.
Public Class Methods
(untyped target, ?strict_excel_mode: bool) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 35 def initialize(target, strict_excel_mode: true) @target = target @strict_excel_mode = strict_excel_mode @io = target.is_a?(String) ? File.open(target, "wb") : target @owns_io = target.is_a?(String) @zip = Ooxml::ZipWriter.new(@io) @sst = [] @sst_index = {} @sheets = [] @current_sheet = nil @current_sheet_index = 0 @current_row_index = 0 @sheet_entry_started = false @current_row_writer = nil @current_columns = [] @current_charts = [] @current_hyperlinks = [] @current_auto_filter = nil @current_filter_columns = {} @current_sort_state = nil @current_data_validations = [] @current_conditional_formats = [] @current_tables = [] @current_pivot_tables = [] @current_sparkline_groups = [] @current_comments = [] @current_merge_cells = [] @current_freeze_pane = nil @current_split_pane = nil @current_selection = nil @current_page_margins = nil @current_page_setup = {} @current_header_footer = {} @current_print_options = {} @current_sheet_protection = nil @current_images = [] @current_shapes = [] @current_sheet_properties = {} @current_sheet_view = {} @current_row_breaks = [] @current_col_breaks = [] @current_cells = {} @styles = {} # { style_name => StyleBuilder } @style_writer = Ooxml::Writer.new @style_name_to_id = {} style("__xlsxrb_date", number_format: "yyyy-mm-dd") style("__xlsxrb_time", number_format: "yyyy-mm-dd hh:mm:ss") # Workbook-level settings @defined_names = [] @core_properties = {} @app_properties = {} @custom_properties = [] @workbook_protection = nil @workbook_properties = { update_links: "never" } end
Initializes a streaming writer context.
@param target [String, IO, StringIO] Destination file path or writable IO stream. @param strict_excel_mode [Boolean] Whether to enforce Microsoft Excel specification limits.
Public Instance Methods
(Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) → void
(Symbol name, String | Integer | Time value) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1465 def app_property(name, value) # simplecov:disable # Edge case / untested delegation block @app_properties[name] = value # simplecov:enable end
Sets an app document property.
@param name [Symbol] Property name. @param value [String, Integer, Time] Property value. @return [void] @api public
(String range) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1041 def auto_filter(range) sheet if @current_sheet.nil? @current_auto_filter = range end
Sets the auto-filter range on the active sheet.
@param range [String] Cell range (e.g. โA1:E100โ). @return [void] @api public
(**untyped options) ?{ (ChartBuilder) → void } → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1003 def chart(**options) sheet if @current_sheet.nil? if block_given? builder = ChartBuilder.new yield builder options = builder.options.merge(options) end @current_charts << options end
Adds a chart to the current worksheet.
@param options [Hash] Chart options. @yield [builder] @yieldparam builder [Xlsxrb::ChartBuilder] @return [void] @api public
() → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1552 def cleanup! if @zip && !@zip.instance_variable_get(:@closed) begin @zip.close rescue StandardError nil end end return unless @owns_io && @io && !@io.closed? begin @io.close rescue StandardError nil end end
Explicitly cleans up any resources if not already closed.
@return [void] @api public
() → untyped
Source
# File lib/xlsxrb/stream_writer.rb, line 1509 def close raise ArgumentError, "Workbook must contain at least one sheet (Excel limitation)" if @strict_excel_mode && @sheets.empty? && @current_sheet.nil? Xlsxrb.in_span("StreamWriter#close") do flush_current_sheet styles_definition = { fonts: @style_writer.fonts.dup, fills: @style_writer.fills.dup, borders: @style_writer.borders.dup, xf_entries: @style_writer.xf_entries.dup, num_fmts: @style_writer.num_fmts.dup, dxfs: @dxfs || [] } resolved_names = resolve_defined_names(@defined_names, @sheets) wb_writer = Ooxml::WorkbookWriter.new( sheets: @sheets, shared_strings: @sst, shared_strings_index: @sst_index, styles: styles_definition, defined_names: resolved_names.empty? ? nil : resolved_names, core_properties: @core_properties.empty? ? nil : @core_properties, app_properties: @app_properties.empty? ? nil : @app_properties, custom_properties: @custom_properties.empty? ? nil : @custom_properties, workbook_protection: @workbook_protection, workbook_properties: @workbook_properties ) wb_writer.write_package_parts(@zip) @zip.close @io.close if @owns_io && !@io.closed? end ensure cleanup! end
Finalizes and writes all streaming sheet contents to the destination target.
@return [void] @api public
(Integer | String | Range[Integer | String] | Array[Integer | String] index, ?width: Float | Integer | nil, ?hidden: bool, ?custom_width: bool, ?outline_level: Integer | nil) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 985 def column(index, width: nil, hidden: false, custom_width: false, outline_level: nil) raise ArgumentError, "Column width #{width} must be between 0 and 255 characters (Excel limitation)" if @strict_excel_mode && width && (width.negative? || width > 255) sheet if @current_sheet.nil? DslHelpers.normalize_column_indices(index).each do |idx| @current_columns << { index: idx, width: width, hidden: hidden, custom_width: custom_width || !width.nil?, outline_level: outline_level } end end
Sets column formatting and properties for one or multiple columns.
@param index [Integer, String, Range, Array] Column index (0-based) or letter (โAโ..โDโ). @param width [Float, Integer, nil] Column width in character units (0 - 255). @param hidden [Boolean] Whether the column is hidden. @param custom_width [Boolean] Whether custom width is set. @param outline_level [Integer, nil] Grouping/outline level. @return [void] @api public
(String | Integer cell, String text, ?author: ::String) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1144 def comment(cell, text, author: "Author") sheet if @current_sheet.nil? @current_comments << { cell: cell, text: text, author: author } end
Adds a comment to a cell.
@param cell [String, Integer] Cell coordinate (e.g. โA1โ). @param text [String] Comment text. @param author [String] Author name. @return [void] @api public
(untyped sqref, **untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1090 def conditional_format(sqref, **opts) sheet if @current_sheet.nil? @current_conditional_formats << opts.merge(sqref: sqref) end
Adds a conditional formatting rule.
@param sqref [String] Cell range. @param opts [Hash] Rule options. @return [void] @api public
(Symbol name, String | Integer | Time value) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1454 def core_property(name, value) @core_properties[name] = value end
Sets a core document metadata property.
@param name [Symbol] Property name. @param value [String, Integer, Time] Property value. @return [void] @api public
(String name, String | Integer | Float | bool | Time value, ?type: ::Symbol) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1497 def custom_property(name, value, type: :string) # simplecov:disable # Edge case / untested delegation block @custom_properties << { name: name, value: value, type: type } # simplecov:enable end
Adds a custom document property.
@param name [String] Property name. @param value [String, Integer, Float, Boolean, Time] Property value. @param type [Symbol] Value type. @return [void] @api public
(String name, String value, ?sheet: String?, ?hidden: bool) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1393 def defined_name(name, value, sheet: nil, hidden: false) entry = { name: name, value: value, hidden: hidden } if sheet # local_sheet_id will be resolved at close time entry[:local_sheet_name] = sheet end @defined_names << entry end
Adds a defined name.
@param name [String] The defined name. @param value [String] The formula or value expression. @param sheet [String, nil] Local sheet name. @param hidden [Boolean] Whether the defined name is hidden. @return [void] @api public
(untyped col_id, untyped filter) → untyped
Source
# File lib/xlsxrb/stream_writer.rb, line 1053 def filter_column(col_id, filter) sheet if @current_sheet.nil? @current_filter_columns[col_id] = filter end
Sets filter criteria on a column in the auto-filter.
@param col_id [Integer] 0-based column index. @param filter [Hash] Filter criteria. @return [void] @api public
(?row: Integer, ?col: (Integer | String)) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1194 def freeze_pane(row: 0, col: 0) col = Elements::Cell.column_index(col) sheet if @current_sheet.nil? @current_freeze_pane = { row: row, col: col } end
Freezes window panes at the given row and column.
@param row [Integer] Number of rows to freeze. @param col [Integer, String] Number of columns to freeze. @return [void] @api public
(String | Integer cell, ?String? url, ?display: String?, ?tooltip: String?, ?location: String?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1025 def hyperlink(cell, url = nil, display: nil, tooltip: nil, location: nil) sheet if @current_sheet.nil? link = { cell: cell } link[:url] = url if url link[:display] = display if display link[:tooltip] = tooltip if tooltip link[:location] = location if location @current_hyperlinks << link end
Adds a hyperlink on a cell.
@param cell [String, Integer] Cell coordinate (e.g. โA1โ). @param url [String, nil] Target URL. @param display [String, nil] Display text. @param tooltip [String, nil] Tooltip text. @param location [String, nil] Internal location. @return [void] @api public
(String file_data, ?ext: ::String, ?from_col: ::Integer, ?from_row: ::Integer, ?to_col: ::Integer, ?to_row: ::Integer, **untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1302 def image(file_data, ext: "png", from_col: 0, from_row: 0, to_col: 5, to_row: 10, **opts) sheet if @current_sheet.nil? img = { file_data: file_data, ext: ext, from_col: from_col, from_row: from_row, to_col: to_col, to_row: to_row } img.merge!(opts) @current_images << img end
Inserts an embedded image into the active sheet.
@param file_data [String] Binary image data. @param ext [String] File extension (โpngโ, โjpegโ, etc.). @param from_col [Integer] Top-left starting column. @param from_row [Integer] Top-left starting row. @param to_col [Integer] Bottom-right ending column. @param to_row [Integer] Bottom-right ending row. @param opts [Hash] Additional options. @return [void] @api public
(?String? name) ?{ (WorksheetProxy) → void } → (WorksheetProxy | nil)
Source
# File lib/xlsxrb/stream_writer.rb, line 823 def internal_sheet_setup(name = nil) flush_current_sheet name ||= "Sheet#{@sheets.size + 1}" @current_sheet = name @current_sheet_index = @sheets.size + 1 @current_row_index = 0 @sheet_entry_started = false @current_row_buffer = String.new(capacity: 65_536) @current_sheet_io = StringIO.new(@current_row_buffer) @current_row_writer = Ooxml::WorksheetWriter.new(@current_sheet_io) @current_row_writer.instance_variable_set(:@started, true) @current_columns = [] @current_charts = [] @current_hyperlinks = [] @current_auto_filter = nil @current_filter_columns = {} @current_sort_state = nil @current_data_validations = [] @current_conditional_formats = [] @current_tables = [] @current_pivot_tables = [] @current_sparkline_groups = [] @current_comments = [] @current_merge_cells = [] @current_freeze_pane = nil @current_split_pane = nil @current_selection = nil @current_page_margins = nil @current_page_setup = {} @current_header_footer = {} @current_print_options = {} @current_sheet_protection = nil @current_images = [] @current_shapes = [] @current_sheet_properties = {} @current_sheet_view = {} @current_row_breaks = [] @current_col_breaks = [] @current_cells = {} return unless block_given? # simplecov:disable # Edge case / untested delegation block yield self flush_current_sheet # simplecov:enable end
Internal: Start or switch to a named sheet (internal helper).
(?(String | Hash[Symbol, Integer | String])? range, ?row: Integer?, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer?, ?row_end: Integer?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1176 def merge(range = nil, row: nil, col_start: nil, col_end: nil, row_start: nil, row_end: nil) sheet if @current_sheet.nil? canonical = DslHelpers.normalize_merge_range( range, row: row, col_start: col_start, col_end: col_end, row_start: row_start, row_end: row_end, strict_excel_mode: @strict_excel_mode ) @current_merge_cells << canonical unless @current_merge_cells.include?(canonical) end
Merges a range of cells into a single cell.
@param range [String, Hash, nil] Cell range (e.g. โA1:B2โ) or hash of coordinates. @param row [Integer, nil] Single row index. @param col_start [Integer, String, nil] Starting column. @param col_end [Integer, String, nil] Ending column. @param row_start [Integer, nil] Starting row index. @param row_end [Integer, nil] Ending row index. @return [void] @api public
(Integer col_index) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1373 def page_break_col(col_index) # simplecov:disable # Edge case / untested delegation block col_index = Elements::Cell.column_index(col_index) sheet if @current_sheet.nil? @current_col_breaks << col_index # simplecov:enable end
Inserts a vertical page break before a column.
@param col_index [Integer, String] 0-based column index or letter (โBโ). @return [void] @api public
(Integer row_num) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1359 def page_break_row(row_num) # simplecov:disable # Edge case / untested delegation block sheet if @current_sheet.nil? @current_row_breaks << row_num # simplecov:enable end
Inserts a horizontal page break before a row.
@param row_num [Integer] 1-based row number. @return [void] @api public
(?left: Float?, ?right: Float?, ?top: Float?, ?bottom: Float?, ?header: Float?, ?footer: Float?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1238 def page_margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil) sheet if @current_sheet.nil? @current_page_margins = DslHelpers.normalize_page_margins( left: left, right: right, top: top, bottom: bottom, header: header, footer: footer ) end
Sets page margins in inches for printing.
@param left [Float, nil] Left margin. @param right [Float, nil] Right margin. @param top [Float, nil] Top margin. @param bottom [Float, nil] Bottom margin. @param header [Float, nil] Header margin. @param footer [Float, nil] Footer margin. @return [void] @api public
(**untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1251 def page_setup(**opts) sheet if @current_sheet.nil? @current_page_setup.merge!(opts) end
Sets page setup configuration.
@param opts [Hash] Page setup options (e.g. orientation: :landscape). @return [void] @api public
(untyped source_ref, row_fields: untyped, data_fields: untyped, ?col_fields: untyped, ?dest_ref: untyped, ?name: untyped, ?field_names: untyped, ?items: untyped, **untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1125 def pivot_table(source_ref, row_fields:, data_fields:, col_fields: [], dest_ref: "E1", name: nil, field_names: nil, items: nil) sheet if @current_sheet.nil? @current_pivot_tables ||= [] @current_pivot_tables << { source_ref: source_ref, row_fields: row_fields, data_fields: data_fields, col_fields: col_fields, dest_ref: dest_ref, name: name, field_names: field_names, items: items } end
Adds a Pivot Table.
@param source_ref [String] Source data range. @param row_fields [Array<Integer>] 0-based field indices for rows. @param data_fields [Array<Hash>] Data fields. @param col_fields [Array<Integer>] 0-based field indices for columns. @param dest_ref [String] Top-left destination cell (default: โE1โ). @param name [String, nil] Pivot table name. @param field_names [Array<String>, nil] Override field names. @param items [Array, nil] Items configuration. @param opts [Hash] Additional options. @return [void] @api public
(String range, ?sheet: String?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1409 def print_area(range, sheet: nil) # simplecov:disable # Edge case / untested delegation block sheet_name = sheet || @current_sheet || "Sheet1" value = "'#{sheet_name}'!#{absolute_range(range)}" @defined_names.reject! { |dn| dn[:name] == "_xlnm.Print_Area" && dn[:local_sheet_name] == sheet_name } defined_name("_xlnm.Print_Area", value, sheet: sheet_name) # simplecov:enable end
Sets the print area for the current or named sheet.
@param range [String] Cell range (e.g. โA1:G50โ). @param sheet [String, nil] Target sheet name. @return [void] @api public
(Symbol name, untyped value) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1274 def print_options(name, value) sheet if @current_sheet.nil? @current_print_options[name] = value end
Sets a print option (e.g. grid_lines: true).
@param name [Symbol] Option name. @param value [Object] Option value. @return [void] @api public
(?rows: String?, ?cols: String?, ?sheet: String?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1427 def print_titles(rows: nil, cols: nil, sheet: nil) sheet_name = sheet || @current_sheet || "Sheet1" parts = [] parts << "'#{sheet_name}'!$#{cols.sub(":", ":$")}" if cols parts << "'#{sheet_name}'!$#{rows.sub(":", ":$")}" if rows value = parts.join(",") @defined_names.reject! { |dn| dn[:name] == "_xlnm.Print_Titles" && dn[:local_sheet_name] == sheet_name } defined_name("_xlnm.Print_Titles", value, sheet: sheet_name) end
Sets print titles for the current or named sheet.
@param rows [String, nil] Repeating row range (e.g. โ1:2โ). @param cols [String, nil] Repeating column range (e.g. โA:Bโ). @param sheet [String, nil] Target sheet name. @return [void] @api public
(?core: Hash[Symbol, String | Integer | Time]?, ?app: Hash[Symbol, String | Integer | Time]?, ?custom: Hash[String | Symbol, untyped]?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1480 def properties(core: nil, app: nil, custom: nil) # simplecov:disable # Edge case / untested delegation block core&.each { |k, v| core_property(k, v) } app&.each { |k, v| app_property(k, v) } custom&.each { |k, v| custom_property(k.to_s, v) } # simplecov:enable end
Sets multiple core and/or app properties.
@param core [Hash, nil] Core properties. @param app [Hash, nil] App properties. @param custom [Hash, nil] Custom properties. @return [void] @api public
(**untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1285 def protect_sheet(**opts) sheet if @current_sheet.nil? @current_sheet_protection = DslHelpers.normalize_protection_options(opts) end
Sets sheet-level protection with optional password hashing.
@param opts [Hash] Protection options. @return [void] @api public
(**String | Integer | bool | nil opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1443 def protect_workbook(**opts) @workbook_protection = opts end
Sets workbook protection.
@param opts [Hash] Protection options. @return [void] @api public
(Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 910 def row(values, styles: nil, height: nil, hidden: false, custom_height: false, outline_level: nil) sheet if @current_sheet.nil? row_index = @current_row_index # See: https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3 if @strict_excel_mode raise ArgumentError, "Row index #{row_index} exceeds Excel limit of 1,048,576 rows" if row_index >= 1_048_576 raise ArgumentError, "Row height #{height} must be between 0 and 409 points (Excel limitation)" if height && (height.negative? || height > 409) end @current_row_index += 1 if values.is_a?(Hash) max_col = values.keys.map { |k| Elements::Cell.column_index(k) }.max || -1 cells_array = Array.new(max_col + 1) values.each do |k, v| idx = Elements::Cell.column_index(k) cells_array[idx] = v end values = cells_array end if styles.is_a?(Hash) expanded_styles = {} styles.each do |k, v| if k.is_a?(Range) || k.is_a?(Array) k.each { |idx| expanded_styles[Elements::Cell.column_index(idx)] = v } else expanded_styles[Elements::Cell.column_index(k)] = v end end max_col_style = expanded_styles.keys.max || -1 styles_array = Array.new(max_col_style + 1) expanded_styles.each do |idx, v| styles_array[idx] = v end styles = styles_array end if @current_charts && !@current_charts.empty? @current_cells ||= {} row_num = row_index + 1 values.each_with_index do |val, col_idx| next if val.nil? addr = "#{Elements::Cell.column_letter(col_idx)}#{row_num}" @current_cells[addr] = val end end raise ArgumentError, "Row contains #{values.length} columns, exceeding Excel limit of 16_384 columns" if @strict_excel_mode && values.length > 16_384 attrs = nil if height || hidden || outline_level attrs = {} attrs[:height] = height if height attrs[:hidden] = true if hidden attrs[:custom_height] = custom_height || !height.nil? attrs[:outline_level] = outline_level if outline_level end @current_row_writer.write_row_values(row_index, values, styles: styles, style_map: @style_name_to_id, sst: @sst, sst_index: @sst_index, attrs: attrs) start_sheet_entry if !@sheet_entry_started && @current_row_buffer.bytesize >= 65_536 end
Appends a row of values to the active sheet.
@param values [Array<Object>, Hash] The cell values. @param styles [String, Symbol, Array, Hash, nil] Style names or inline style definitions. @param height [Float, Integer, nil] The row height in points (0 - 409). @param hidden [Boolean] Whether the row is hidden. @param custom_height [Boolean] Whether custom row height is set. @param outline_level [Integer, nil] Grouping/outline level (0 - 7). @return [void] @api public
(String active_cell, ?sqref: String?, ?pane: (String | Symbol)?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1221 def select_cell(active_cell, sqref: nil, pane: nil) sheet if @current_sheet.nil? @current_selection = { active_cell: active_cell, sqref: sqref || active_cell } @current_selection[:pane] = pane if pane end
Sets the active cell selection on the current sheet.
@param active_cell [String] Active cell reference (e.g. โA1โ). @param sqref [String, nil] Selection range. @param pane [String, Symbol, nil] Target pane. @return [void] @api public
(**untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1321 def shape(preset: "rect", text: nil, from_col: 0, from_row: 0, to_col: 5, to_row: 5, **opts) sheet if @current_sheet.nil? shape = { preset: preset, text: text, from_col: from_col, from_row: from_row, to_col: to_col, to_row: to_row } shape[:name] = opts.delete(:name) || "Shape #{@current_shapes.size + 1}" shape.merge!(opts) @current_shapes << shape end
Inserts a drawing shape.
@param preset [String] Preset shape name. @param text [String, nil] Label text. @param from_col [Integer] Top-left column. @param from_row [Integer] Top-left row. @param to_col [Integer] Bottom-right column. @param to_row [Integer] Bottom-right row. @param opts [Hash] Additional options. @return [void] @api public
(?String? name, **untyped opts) ?{ (WorksheetProxy) → void } → untyped
Source
# File lib/xlsxrb/stream_writer.rb, line 808 def sheet(name = nil, **opts) name ||= "Sheet#{@sheets.size + 1}" raise ArgumentError, "Sheet name '#{name}' must be <= 31 characters (Excel limitation)" if @strict_excel_mode && name.length > 31 raise ArgumentError, "Sheet name '#{name}' contains invalid characters (ECMA-376 OOXML specification)" if name.match?(%r{[\[\]*?/\\]}) raise ArgumentError, "Sheet name '#{name}' is already used. Excel requires unique sheet names." if @strict_excel_mode && @sheets.map { |s| s.respond_to?(:name) ? s.name.downcase : s.to_s.downcase }.include?(name.downcase) internal_sheet_setup(name) opts.each { |k, v| sheet_properties(k, v) } yield WorksheetProxy.new(self, @current_sheet) if block_given? @current_sheet end
Adds a new sheet to the workbook and starts streaming rows into it.
@param name [String, nil] Sheet name (max 31 characters). @param opts [Hash] Sheet-level configuration. @yield [sheet_proxy] @yieldparam sheet_proxy [WorksheetProxy] The streaming sheet proxy. @return [String] The sheet name. @api public
(Symbol name, untyped value) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1336 def sheet_properties(name, value) sheet if @current_sheet.nil? @current_sheet_properties[name] = value end
Sets a sheet-level property (e.g. tab_color: โFF0000โ).
@param name [Symbol] Property name. @param value [Object] Property value. @return [void] @api public
(Symbol name, untyped value) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1348 def sheet_view(name, value) sheet if @current_sheet.nil? @current_sheet_view[name] = value end
Sets a sheet view property (e.g. zoom_scale: 120).
@param name [Symbol] View property name. @param value [Object] View property value. @return [void] @api public
(untyped ref, untyped sort_conditions, **untyped opts) → untyped
Source
# File lib/xlsxrb/stream_writer.rb, line 1066 def sort_state(ref, sort_conditions, **opts) sheet if @current_sheet.nil? @current_sort_state = { ref: ref, sort_conditions: sort_conditions }.merge(opts) end
Configures column sort state on the active sheet.
@param ref [String] The sorted range. @param sort_conditions [Array<Hash>] Sort conditions. @param opts [Hash] Additional options. @return [void] @api public
(sparklines: untyped, ?type: untyped, **untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1157 def sparkline_group(sparklines:, type: nil, **opts) sheet if @current_sheet.nil? group = { sparklines: sparklines } group[:type] = type if type group.merge!(opts) @current_sparkline_groups << group end
Adds a sparkline group.
@param sparklines [Array<Hash>] Sparkline definitions. @param type [String, nil] โlineโ, โcolumnโ, or โstackedโ. @param opts [Hash] Additional options. @return [void] @api public
(?x_split: ::Integer, ?y_split: ::Integer, ?top_left_cell: String?) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1208 def split_pane(x_split: 0, y_split: 0, top_left_cell: nil) sheet if @current_sheet.nil? @current_split_pane = { x_split: x_split, y_split: y_split, top_left_cell: top_left_cell } end
Splits window panes without freezing.
@param x_split [Integer] Horizontal split in points. @param y_split [Integer] Vertical split in points. @param top_left_cell [String, nil] Top-left cell reference in bottom-right pane. @return [void] @api public
() → untyped
Source
# File lib/xlsxrb/stream_writer.rb, line 873 def start_sheet_entry return if @sheet_entry_started @sheet_entry_started = true @zip.start_entry("xl/worksheets/sheet#{@current_sheet_index}.xml") zip_io = Ooxml::WorkbookWriter::ZipEntryIO.new(@zip) row_buf = @current_row_writer.instance_variable_get(:@row_buffer) if row_buf && !row_buf.empty? @current_sheet_io.write(row_buf) row_buf.clear end @current_row_writer = Ooxml::WorksheetWriter.new(zip_io) @current_row_writer.start( columns: @current_columns, sheet_properties: @current_sheet_properties.empty? ? nil : @current_sheet_properties, freeze_pane: @current_freeze_pane, split_pane: @current_split_pane, selection: @current_selection, sheet_view: @current_sheet_view.empty? ? nil : @current_sheet_view ) @zip.write_data(@current_row_buffer) unless @current_row_buffer.empty? @current_row_buffer.clear end
(String | Symbol name, **untyped opts) ?{ (StyleBuilder) → void } → StyleBuilder
Source
# File lib/xlsxrb/stream_writer.rb, line 113 def style(name, **opts) style_name = name.to_s style_builder = StyleBuilder.new(style_name) style_builder.apply_options!(**opts) unless opts.empty? yield style_builder if block_given? @styles[style_name] = style_builder # Register immediately with low-level style writer @style_name_to_id[style_name] = style_builder.register_with(@style_writer) style_builder end
Defines or configures a named cell style.
@param name [String, Symbol] The name of the style. @param opts [Hash] Style options (e.g. bold: true, fill_color: โFF0000โ). @yield [style_builder] @yieldparam style_builder [Xlsxrb::StyleBuilder] @return [StyleBuilder]
(untyped ref, columns: untyped, ?name: untyped, ?display_name: untyped, ?style: untyped, **untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1106 def table(ref, columns:, name: nil, display_name: nil, style: nil, **) sheet if @current_sheet.nil? @current_tables << DslHelpers.normalize_table_options(ref, columns: columns, name: name, display_name: display_name, style: style, **) end
Adds an Excel Table (ListObject).
@param ref [String] Table range. @param columns [Array<String>] Column names. @param name [String, nil] Table name. @param display_name [String, nil] Display name. @param style [String, nil] Table style. @param opts [Hash] Additional options. @return [void] @api public
(untyped sqref, **untyped opts) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 1078 def validate_data(sqref, **opts) sheet if @current_sheet.nil? @current_data_validations << opts.merge(sqref: sqref) end
Adds a data validation rule.
@param sqref [String] Cell range. @param opts [Hash] Validation options. @return [void] @api public
(Symbol name, String | Integer | bool value) → void
Source
# File lib/xlsxrb/stream_writer.rb, line 101 def workbook_property(name, value) @workbook_properties[name] = value end
Sets a workbook property.
@note **SECURITY WARNING:** If you set :update_links to anything other than โโneverโ`,
you may expose end-users to malicious external reference vulnerabilities (e.g., CSV/DDE Injection) when they open the generated Excel file. Ensure you fully trust the exported data.
@param name [Symbol] Property name (e.g. :update_links). @param value [String, Integer, Boolean] Property value. @return [void]