Class: Xlsxrb::WorkbookBuilder
- Inherits:
-
Object
- Object
- Xlsxrb::WorkbookBuilder
- Defined in:
- lib/xlsxrb.rb,
sig/generated/xlsxrb.rbs
Overview
DSL context for Xlsxrb.build.
Instance Method Summary collapse
-
#absolute_range(range) ⇒ Object
: (untyped range) -> untyped.
-
#app_property(name, value) ⇒ void
Set an app document property.
-
#build ⇒ Elements::Workbook
Builds and returns the in-memory Elements::Workbook.
-
#core_property(name, value) ⇒ void
Set a core document property.
-
#custom_property(name, value, type: :string) ⇒ void
Add a custom document property.
-
#defined_name(name, value, sheet: nil, hidden: false) ⇒ void
Add a defined name.
-
#extract_styles_from_writer(writer) ⇒ { fonts: untyped, fills: untyped, borders: untyped, xf_entries: untyped, num_fmts: untyped }
: (untyped writer) -> { fonts: untyped, fills: untyped, borders: untyped, xf_entries: untyped, num_fmts: untyped }.
-
#initialize(strict_excel_mode: true) ⇒ WorkbookBuilder
constructor
: (?strict_excel_mode: bool) -> void.
-
#print_area(range, sheet: nil) ⇒ void
Set the print area for a sheet.
-
#print_titles(rows: nil, cols: nil, sheet: nil) ⇒ void
Set print titles for a sheet.
-
#process_styles(sheets) ⇒ ::Array[untyped | ::Hash[untyped, untyped]], ::Array[untyped]
: (untyped sheets) -> (::Array[untyped | ::Hash[untyped, untyped]] | ::Array).
-
#properties(core: nil, app: nil) ⇒ void
Set multiple core and/or app properties.
-
#protect_workbook(**opts) ⇒ void
Set workbook protection.
-
#resolve_defined_names(names, sheets) ⇒ Object
: (untyped names, untyped sheets) -> untyped.
-
#sheet(name = nil, **opts) {|sheet_builder| ... } ⇒ void
(also: #[])
Add a new sheet.
-
#workbook_property(name, value) ⇒ void
Set a workbook property.
Constructor Details
#initialize(strict_excel_mode: true) ⇒ WorkbookBuilder
: (?strict_excel_mode: bool) -> void
656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/xlsxrb.rb', line 656 def initialize(strict_excel_mode: true) @strict_excel_mode = strict_excel_mode @sheets = [] @sheet_builders = [] # Keep track of sheet builders for style processing @defined_names = [] @core_properties = {} @app_properties = {} @custom_properties = [] @workbook_protection = nil @workbook_properties = { update_links: "never" } end |
Instance Method Details
#absolute_range(range) ⇒ Object
: (untyped range) -> untyped
841 842 843 |
# File 'lib/xlsxrb.rb', line 841 def absolute_range(range) range.gsub(/([A-Z]+)(\d+)/, '$\1$\2') end |
#app_property(name, value) ⇒ void
This method returns an undefined value.
Set an app document property.
: (Symbol name, String | Integer | Time value) -> void
783 784 785 |
# File 'lib/xlsxrb.rb', line 783 def app_property(name, value) @app_properties[name] = value end |
#build ⇒ Elements::Workbook
Builds and returns the in-memory Elements::Workbook.
: () -> Elements::Workbook
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 |
# File 'lib/xlsxrb.rb', line 816 def build raise ArgumentError, "Workbook must contain at least one sheet (Excel limitation)" if @strict_excel_mode && @sheets.empty? # Process styles from all sheets and collect style definitions processed_sheets, styles_definition = process_styles(@sheets) # Store workbook-level metadata in unmapped_data = {} [:defined_names] = resolve_defined_names(@defined_names, processed_sheets) unless @defined_names.empty? [:core_properties] = @core_properties unless @core_properties.empty? [:app_properties] = @app_properties unless @app_properties.empty? [:custom_properties] = @custom_properties unless @custom_properties.empty? [:workbook_protection] = @workbook_protection if @workbook_protection [:workbook_properties] = @workbook_properties unless @workbook_properties.empty? Elements::Workbook.new( sheets: processed_sheets, styles: styles_definition, unmapped_data: .empty? ? {} : { facade: } ) end |
#core_property(name, value) ⇒ void
This method returns an undefined value.
Set a core document property.
: (Symbol name, String | Integer | Time value) -> void
772 773 774 |
# File 'lib/xlsxrb.rb', line 772 def core_property(name, value) @core_properties[name] = value end |
#custom_property(name, value, type: :string) ⇒ void
This method returns an undefined value.
Add a custom document property.
: (String name, String | Integer | Float | bool | Time value, ?type: ::Symbol) -> void
807 808 809 |
# File 'lib/xlsxrb.rb', line 807 def custom_property(name, value, type: :string) @custom_properties << { name: name, value: value, type: type } end |
#defined_name(name, value, sheet: nil, hidden: false) ⇒ void
This method returns an undefined value.
Add a defined name.
: (String name, String value, ?sheet: String?, ?hidden: bool) -> void
717 718 719 720 721 |
# File 'lib/xlsxrb.rb', line 717 def defined_name(name, value, sheet: nil, hidden: false) entry = { name: name, value: value, hidden: hidden } entry[:local_sheet_name] = sheet if sheet @defined_names << entry end |
#extract_styles_from_writer(writer) ⇒ { fonts: untyped, fills: untyped, borders: untyped, xf_entries: untyped, num_fmts: untyped }
: (untyped writer) -> { fonts: untyped, fills: untyped, borders: untyped, xf_entries: untyped, num_fmts: untyped }
926 927 928 929 930 931 932 933 934 935 936 |
# File 'lib/xlsxrb.rb', line 926 def extract_styles_from_writer(writer) # Extract style definitions from the writer that can be reused # This captures the fonts, fills, borders, and xf entries that were created { fonts: writer.fonts.dup, fills: writer.fills.dup, borders: writer.borders.dup, xf_entries: writer.xf_entries.dup, num_fmts: writer.num_fmts.dup } end |
#print_area(range, sheet: nil) ⇒ void
This method returns an undefined value.
Set the print area for a sheet.
: (String range, ?sheet: String?) -> void
730 731 732 733 734 735 |
# File 'lib/xlsxrb.rb', line 730 def print_area(range, sheet: nil) sheet_name = sheet || @sheets.last&.name || "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) end |
#print_titles(rows: nil, cols: nil, sheet: nil) ⇒ void
This method returns an undefined value.
Set print titles for a sheet.
: (?rows: String?, ?cols: String?, ?sheet: String?) -> void
745 746 747 748 749 750 751 752 753 |
# File 'lib/xlsxrb.rb', line 745 def print_titles(rows: nil, cols: nil, sheet: nil) sheet_name = sheet || @sheets.last&.name || "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 |
#process_styles(sheets) ⇒ ::Array[untyped | ::Hash[untyped, untyped]], ::Array[untyped]
: (untyped sheets) -> (::Array[untyped | ::Hash[untyped, untyped]] | ::Array)
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 |
# File 'lib/xlsxrb.rb', line 860 def process_styles(sheets) # Collect all unique StyleBuilders from all sheets all_style_builders = {} @sheet_builders.each do |sheet_builder| sheet_builder.styles.each do |style_name, style_builder| all_style_builders[style_name] = style_builder end end return [sheets, {}] if all_style_builders.empty? # Create a temporary writer to register styles and get numeric IDs temp_writer = Ooxml::Writer.new style_name_to_id = {} all_style_builders.each do |name, builder| style_id = builder.register_with(temp_writer) style_name_to_id[name] = style_id end # Capture the style definitions from the temporary writer styles_definition = extract_styles_from_writer(temp_writer) # Update all cells with their resolved style IDs updated_sheets = sheets.map do |sheet| new_rows = sheet.rows.map do |row| new_cells = row.cells.map do |cell| # If style_index is a string (style name), resolve it to a numeric ID if cell.style_index.is_a?(String) && style_name_to_id.key?(cell.style_index) Elements::Cell.new( row_index: cell.row_index, column_index: cell.column_index, value: cell.value, formula: cell.formula, style_index: style_name_to_id[cell.style_index], unmapped_data: cell.unmapped_data, errors: cell.errors ) else cell end end Elements::Row.new( index: row.index, cells: new_cells, height: row.height, hidden: row.hidden, custom_height: row.custom_height, outline_level: row.outline_level, unmapped_data: row.unmapped_data, errors: row.errors ) end Elements::Worksheet.new( name: sheet.name, rows: new_rows, columns: sheet.columns, charts: sheet.charts, unmapped_data: sheet.unmapped_data, errors: sheet.errors ) end [updated_sheets, styles_definition] end |
#properties(core: nil, app: nil) ⇒ void
This method returns an undefined value.
Set multiple core and/or app properties.
: (?core: Hash[Symbol, String | Integer | Time]?, ?app: Hash[Symbol, String | Integer | Time]?) -> void
794 795 796 797 |
# File 'lib/xlsxrb.rb', line 794 def properties(core: nil, app: nil) core&.each { |k, v| core_property(k, v) } app&.each { |k, v| app_property(k, v) } end |
#protect_workbook(**opts) ⇒ void
This method returns an undefined value.
Set workbook protection.
: (**String | Integer | bool | nil opts) -> void
761 762 763 |
# File 'lib/xlsxrb.rb', line 761 def protect_workbook(**opts) @workbook_protection = opts end |
#resolve_defined_names(names, sheets) ⇒ Object
: (untyped names, untyped sheets) -> untyped
846 847 848 849 850 851 852 853 854 855 856 857 |
# File 'lib/xlsxrb.rb', line 846 def resolve_defined_names(names, sheets) sheet_names = sheets.map(&:name) names.map do |dn| resolved = dn.dup if dn[:local_sheet_name] idx = sheet_names.index(dn[:local_sheet_name]) resolved[:local_sheet_id] = idx if idx resolved.delete(:local_sheet_name) end resolved end end |
#sheet(name = nil, **opts) {|sheet_builder| ... } ⇒ void Also known as: []
This method returns an undefined value.
Add a new sheet.
: (?String? name, **untyped opts) ?{ (WorksheetBuilder) -> void } -> untyped
692 693 694 695 696 697 698 699 700 701 702 703 |
# File 'lib/xlsxrb.rb', line 692 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) sheet_builder = WorksheetBuilder.new(name, strict_excel_mode: @strict_excel_mode) opts.each { |k, v| sheet_builder.sheet_properties(k, v) } yield sheet_builder if block_given? @sheet_builders << sheet_builder @sheets << sheet_builder.build end |
#workbook_property(name, value) ⇒ void
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.
This method returns an undefined value.
Set a workbook property.
: (Symbol name, String | Integer | bool value) -> (String | Integer | bool)
679 680 681 |
# File 'lib/xlsxrb.rb', line 679 def workbook_property(name, value) @workbook_properties[name] = value end |