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
714 715 716 717 718 719 720 721 722 723 724 |
# File 'lib/xlsxrb.rb', line 714 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
899 900 901 |
# File 'lib/xlsxrb.rb', line 899 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
841 842 843 |
# File 'lib/xlsxrb.rb', line 841 def app_property(name, value) @app_properties[name] = value end |
#build ⇒ Elements::Workbook
Builds and returns the in-memory Elements::Workbook.
: () -> Elements::Workbook
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 |
# File 'lib/xlsxrb.rb', line 874 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
830 831 832 |
# File 'lib/xlsxrb.rb', line 830 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
865 866 867 |
# File 'lib/xlsxrb.rb', line 865 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
775 776 777 778 779 |
# File 'lib/xlsxrb.rb', line 775 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 }
984 985 986 987 988 989 990 991 992 993 994 |
# File 'lib/xlsxrb.rb', line 984 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
788 789 790 791 792 793 |
# File 'lib/xlsxrb.rb', line 788 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
803 804 805 806 807 808 809 810 811 |
# File 'lib/xlsxrb.rb', line 803 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)
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 |
# File 'lib/xlsxrb.rb', line 918 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
852 853 854 855 |
# File 'lib/xlsxrb.rb', line 852 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
819 820 821 |
# File 'lib/xlsxrb.rb', line 819 def protect_workbook(**opts) @workbook_protection = opts end |
#resolve_defined_names(names, sheets) ⇒ Object
: (untyped names, untyped sheets) -> untyped
904 905 906 907 908 909 910 911 912 913 914 915 |
# File 'lib/xlsxrb.rb', line 904 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
750 751 752 753 754 755 756 757 758 759 760 761 |
# File 'lib/xlsxrb.rb', line 750 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)
737 738 739 |
# File 'lib/xlsxrb.rb', line 737 def workbook_property(name, value) @workbook_properties[name] = value end |