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
805 806 807 808 809 810 811 812 813 814 815 |
# File 'lib/xlsxrb.rb', line 805 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
990 991 992 |
# File 'lib/xlsxrb.rb', line 990 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
932 933 934 |
# File 'lib/xlsxrb.rb', line 932 def app_property(name, value) @app_properties[name] = value end |
#build ⇒ Elements::Workbook
Builds and returns the in-memory Elements::Workbook.
: () -> Elements::Workbook
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 |
# File 'lib/xlsxrb.rb', line 965 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
921 922 923 |
# File 'lib/xlsxrb.rb', line 921 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
956 957 958 |
# File 'lib/xlsxrb.rb', line 956 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
866 867 868 869 870 |
# File 'lib/xlsxrb.rb', line 866 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 }
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
# File 'lib/xlsxrb.rb', line 1075 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
879 880 881 882 883 884 |
# File 'lib/xlsxrb.rb', line 879 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
894 895 896 897 898 899 900 901 902 |
# File 'lib/xlsxrb.rb', line 894 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)
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 |
# File 'lib/xlsxrb.rb', line 1009 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
943 944 945 946 |
# File 'lib/xlsxrb.rb', line 943 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
910 911 912 |
# File 'lib/xlsxrb.rb', line 910 def protect_workbook(**opts) @workbook_protection = opts end |
#resolve_defined_names(names, sheets) ⇒ Object
: (untyped names, untyped sheets) -> untyped
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 |
# File 'lib/xlsxrb.rb', line 995 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
841 842 843 844 845 846 847 848 849 850 851 852 |
# File 'lib/xlsxrb.rb', line 841 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)
828 829 830 |
# File 'lib/xlsxrb.rb', line 828 def workbook_property(name, value) @workbook_properties[name] = value end |