zaxcel 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.rspec +4 -0
 - data/.rubocop.yml +9 -0
 - data/CHANGELOG.md +29 -0
 - data/CONTRIBUTING.md +110 -0
 - data/LICENSE +22 -0
 - data/QUICK_START.md +187 -0
 - data/README.md +372 -0
 - data/Rakefile +18 -0
 - data/SETUP.md +178 -0
 - data/lib/enumerable.rb +47 -0
 - data/lib/zaxcel/README.md +37 -0
 - data/lib/zaxcel/arithmetic.rb +88 -0
 - data/lib/zaxcel/binary_expression.rb +74 -0
 - data/lib/zaxcel/binary_expressions/addition.rb +36 -0
 - data/lib/zaxcel/binary_expressions/division.rb +24 -0
 - data/lib/zaxcel/binary_expressions/multiplication.rb +24 -0
 - data/lib/zaxcel/binary_expressions/subtraction.rb +41 -0
 - data/lib/zaxcel/binary_expressions.rb +38 -0
 - data/lib/zaxcel/cell.rb +141 -0
 - data/lib/zaxcel/cell_formula.rb +16 -0
 - data/lib/zaxcel/column.rb +142 -0
 - data/lib/zaxcel/document.rb +136 -0
 - data/lib/zaxcel/function.rb +6 -0
 - data/lib/zaxcel/functions/abs.rb +18 -0
 - data/lib/zaxcel/functions/and.rb +23 -0
 - data/lib/zaxcel/functions/average.rb +17 -0
 - data/lib/zaxcel/functions/choose.rb +20 -0
 - data/lib/zaxcel/functions/concatenate.rb +20 -0
 - data/lib/zaxcel/functions/if.rb +38 -0
 - data/lib/zaxcel/functions/if_error.rb +25 -0
 - data/lib/zaxcel/functions/index.rb +20 -0
 - data/lib/zaxcel/functions/len.rb +16 -0
 - data/lib/zaxcel/functions/match/match_type.rb +13 -0
 - data/lib/zaxcel/functions/match.rb +27 -0
 - data/lib/zaxcel/functions/max.rb +17 -0
 - data/lib/zaxcel/functions/min.rb +17 -0
 - data/lib/zaxcel/functions/negate.rb +26 -0
 - data/lib/zaxcel/functions/or.rb +23 -0
 - data/lib/zaxcel/functions/round.rb +20 -0
 - data/lib/zaxcel/functions/sum.rb +18 -0
 - data/lib/zaxcel/functions/sum_if.rb +20 -0
 - data/lib/zaxcel/functions/sum_ifs.rb +34 -0
 - data/lib/zaxcel/functions/sum_product.rb +18 -0
 - data/lib/zaxcel/functions/text.rb +17 -0
 - data/lib/zaxcel/functions/unique.rb +23 -0
 - data/lib/zaxcel/functions/x_lookup.rb +28 -0
 - data/lib/zaxcel/functions/xirr.rb +27 -0
 - data/lib/zaxcel/functions.rb +169 -0
 - data/lib/zaxcel/if_builder.rb +22 -0
 - data/lib/zaxcel/lang.rb +23 -0
 - data/lib/zaxcel/reference.rb +28 -0
 - data/lib/zaxcel/references/cell.rb +42 -0
 - data/lib/zaxcel/references/column.rb +49 -0
 - data/lib/zaxcel/references/range.rb +35 -0
 - data/lib/zaxcel/references/row.rb +34 -0
 - data/lib/zaxcel/references.rb +5 -0
 - data/lib/zaxcel/roundable.rb +14 -0
 - data/lib/zaxcel/row.rb +93 -0
 - data/lib/zaxcel/sheet.rb +425 -0
 - data/lib/zaxcel/sorbet/enumerizable_enum.rb +50 -0
 - data/lib/zaxcel/version.rb +6 -0
 - data/lib/zaxcel.rb +73 -0
 - data/zaxcel.gemspec +73 -0
 - metadata +266 -0
 
| 
         @@ -0,0 +1,136 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Document
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              MAX_SHEET_NAME_LENGTH = 31
         
     | 
| 
      
 8 
     | 
    
         
            +
              DEFAULT_WIDTH_UNITS_BY_DEFAULT_CHARACTER = 0.85 # empirically determined for 11pt body Calibri
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              sig { returns(Float) }
         
     | 
| 
      
 11 
     | 
    
         
            +
              attr_reader :width_units_by_default_character
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              sig { params(width_units_by_default_character: Float).void }
         
     | 
| 
      
 14 
     | 
    
         
            +
              def initialize(width_units_by_default_character: DEFAULT_WIDTH_UNITS_BY_DEFAULT_CHARACTER)
         
     | 
| 
      
 15 
     | 
    
         
            +
                @document = T.let(Axlsx::Package.new, Axlsx::Package)
         
     | 
| 
      
 16 
     | 
    
         
            +
                @document.workbook.escape_formulas = false
         
     | 
| 
      
 17 
     | 
    
         
            +
                @width_units_by_default_character = width_units_by_default_character
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              sig { params(sheet: Axlsx::Worksheet, range_to_include: String).void }
         
     | 
| 
      
 21 
     | 
    
         
            +
              def set_print_area(sheet:, range_to_include:)
         
     | 
| 
      
 22 
     | 
    
         
            +
                @document.workbook.add_defined_name(range_to_include.to_s, local_sheet_id: sheet.index, name: '_xlnm.Print_Area')
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              sig { params(sheet_name: String, sheet_visibility: Zaxcel::Sheet::SheetVisibility).returns(Zaxcel::Sheet) }
         
     | 
| 
      
 26 
     | 
    
         
            +
              def add_sheet!(sheet_name, sheet_visibility: Zaxcel::Sheet::SheetVisibility::Visible)
         
     | 
| 
      
 27 
     | 
    
         
            +
                clean_sheet_name = clean_sheet_name(sheet_name)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                worksheet = @document.workbook.add_worksheet({ name: clean_sheet_name })
         
     | 
| 
      
 30 
     | 
    
         
            +
                worksheet.page_setup.fit_to(width: 1, height: 1)
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                # This is necessary to show grouped columns, etc. The default value is false, which is inconsistent with new
         
     | 
| 
      
 33 
     | 
    
         
            +
                # new documents created in excel. See https://github.com/caxlsx/caxlsx/issues/344 for additional context.
         
     | 
| 
      
 34 
     | 
    
         
            +
                worksheet.sheet_view.show_outline_symbols = true
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                sheet = Zaxcel::Sheet.new(name: sheet_name, document: self, worksheet: worksheet, visibility: sheet_visibility)
         
     | 
| 
      
 37 
     | 
    
         
            +
                sheet_by_name[sheet_name] = sheet
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                sheet
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              sig { params(name: Symbol).returns(T.nilable(Integer)) }
         
     | 
| 
      
 43 
     | 
    
         
            +
              def style(name)
         
     | 
| 
      
 44 
     | 
    
         
            +
                styles_by_style_name[name]
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              sig { params(name: Symbol, kwargs: T.untyped).returns(Integer) }
         
     | 
| 
      
 48 
     | 
    
         
            +
              def add_style!(name, **kwargs)
         
     | 
| 
      
 49 
     | 
    
         
            +
                styles_by_style_name[name] ||= @document.workbook.styles.add_style(**kwargs)
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              sig { returns(T::Hash[Symbol, Integer]) }
         
     | 
| 
      
 53 
     | 
    
         
            +
              def styles_by_style_name
         
     | 
| 
      
 54 
     | 
    
         
            +
                @styles ||= T.let({}, T.nilable(T::Hash[Symbol, Integer]))
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              sig { params(name: String).returns(T.nilable(Zaxcel::Sheet)) }
         
     | 
| 
      
 58 
     | 
    
         
            +
              def sheet(name)
         
     | 
| 
      
 59 
     | 
    
         
            +
                sheet_by_name[name]
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              sig { returns(T::Hash[String, Zaxcel::Sheet]) }
         
     | 
| 
      
 63 
     | 
    
         
            +
              def sheet_by_name
         
     | 
| 
      
 64 
     | 
    
         
            +
                @sheet_by_name ||= T.let({}, T.nilable(T::Hash[String, Zaxcel::Sheet]))
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              sig { returns(T.nilable(String)) }
         
     | 
| 
      
 68 
     | 
    
         
            +
              def file_contents
         
     | 
| 
      
 69 
     | 
    
         
            +
                stream = T.cast(@document.to_stream, T.any(StringIO, T::Boolean))
         
     | 
| 
      
 70 
     | 
    
         
            +
                return if !stream.is_a?(StringIO)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                stream.read
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              # this exposes the underlying axlsx document directly. it's an escape hatch for modifying the document in ways not
         
     | 
| 
      
 76 
     | 
    
         
            +
              # directly supported by zaxcel. it is unsafe in that it can break zaxcel's assumptions about the document leading to
         
     | 
| 
      
 77 
     | 
    
         
            +
              # unintended consequences. use with caution.
         
     | 
| 
      
 78 
     | 
    
         
            +
              sig { returns(Axlsx::Package) }
         
     | 
| 
      
 79 
     | 
    
         
            +
              def unsafe_axlsx_document
         
     | 
| 
      
 80 
     | 
    
         
            +
                @document
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              sig { params(sheet_name: String, pending_sheet_names: T::Array[String]).returns(String) }
         
     | 
| 
      
 84 
     | 
    
         
            +
              def clean_sheet_name(sheet_name, pending_sheet_names: [])
         
     | 
| 
      
 85 
     | 
    
         
            +
                # sheet names in excel must
         
     | 
| 
      
 86 
     | 
    
         
            +
                # 1. be max 31 charaters
         
     | 
| 
      
 87 
     | 
    
         
            +
                # 2. not special characters
         
     | 
| 
      
 88 
     | 
    
         
            +
                # 3. be unique
         
     | 
| 
      
 89 
     | 
    
         
            +
                cleaned_sheet_name = sheet_name.gsub(/[^0-9a-z_ ]/i, '').parameterize(
         
     | 
| 
      
 90 
     | 
    
         
            +
                  separator: ' ',
         
     | 
| 
      
 91 
     | 
    
         
            +
                  preserve_case: true,
         
     | 
| 
      
 92 
     | 
    
         
            +
                ).first(MAX_SHEET_NAME_LENGTH)
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                unique_index = 0
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                unique_name = cleaned_sheet_name
         
     | 
| 
      
 97 
     | 
    
         
            +
                while @document.workbook.sheet_by_name(unique_name).present? || pending_sheet_names.include?(unique_name)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  unique_index += 1
         
     | 
| 
      
 99 
     | 
    
         
            +
                  unique_name = "#{cleaned_sheet_name.first(cleaned_sheet_name.length - unique_index.to_s.length)}#{unique_index}"
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                unique_name
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 106 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                sig { params(start_reference: Zaxcel::References::Cell, end_reference: Zaxcel::References::Cell).returns(T::Array[Zaxcel::Cell]) }
         
     | 
| 
      
 109 
     | 
    
         
            +
                def cells_in_range(start_reference:, end_reference:)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  cell_start = T.must(start_reference.cell)
         
     | 
| 
      
 111 
     | 
    
         
            +
                  cell_end = T.must(end_reference.cell)
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  if cell_start.column == cell_end.column
         
     | 
| 
      
 114 
     | 
    
         
            +
                    column = cell_start.column
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    cells_in_column = column.cell_by_row_name.values
         
     | 
| 
      
 117 
     | 
    
         
            +
                    cells_in_column.select do |cell|
         
     | 
| 
      
 118 
     | 
    
         
            +
                      row_position = T.must(cell.row_position)
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                      row_position >= T.must(cell_start.row_position) && row_position <= T.must(cell_end.row_position)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
                  elsif cell_start.row == cell_end.row
         
     | 
| 
      
 123 
     | 
    
         
            +
                    row = cell_start.row
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    cells_in_row = row.cell_by_column_name.values
         
     | 
| 
      
 126 
     | 
    
         
            +
                    cells_in_row.select do |cell|
         
     | 
| 
      
 127 
     | 
    
         
            +
                      col_position = T.must(cell.col_position)
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                      col_position >= T.must(cell_start.col_position) && col_position <= T.must(cell_end.col_position)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
                  else
         
     | 
| 
      
 132 
     | 
    
         
            +
                    raise 'Cells must be in the same row or column in order to sum'
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,6 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # Functions don't have any special interface beyond cell formulas, but it's useful to be able to detect them since they
         
     | 
| 
      
 5 
     | 
    
         
            +
            # interact with other cell formulas in their own way.
         
     | 
| 
      
 6 
     | 
    
         
            +
            class Zaxcel::Function < Zaxcel::CellFormula; end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Abs < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(value: Zaxcel::Cell::ValueType).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(value)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                formatted_value = Zaxcel::Cell.format(@value, on_sheet: on_sheet) || 0
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                "ABS(#{formatted_value})"
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::And < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              # todo - likely and can accept more than one arg, but not sure. check into this.
         
     | 
| 
      
 8 
     | 
    
         
            +
              # also, it may accept only specific types of formulas (returns true or false or something), but my suspicion is that
         
     | 
| 
      
 9 
     | 
    
         
            +
              # is not true. confirm this also, since right now it's typed to a strange type.
         
     | 
| 
      
 10 
     | 
    
         
            +
              sig { params(left_hand_value: Zaxcel::Cell::ValueType, right_hand_value: Zaxcel::Cell::ValueType).void }
         
     | 
| 
      
 11 
     | 
    
         
            +
              def initialize(left_hand_value, right_hand_value)
         
     | 
| 
      
 12 
     | 
    
         
            +
                @left_hand_value = left_hand_value
         
     | 
| 
      
 13 
     | 
    
         
            +
                @right_hand_value = right_hand_value
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 17 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 18 
     | 
    
         
            +
                left_hand_formatted_value = Zaxcel::Cell.format(@left_hand_value, on_sheet: on_sheet)
         
     | 
| 
      
 19 
     | 
    
         
            +
                right_hand_formatted_value = Zaxcel::Cell.format(@right_hand_value, on_sheet: on_sheet)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                "AND(#{left_hand_formatted_value},#{right_hand_formatted_value})"
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Average < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              # todo - match this to the excel signature if it takes an unbounded argument array
         
     | 
| 
      
 8 
     | 
    
         
            +
              sig { params(range: Zaxcel::References::Range).void }
         
     | 
| 
      
 9 
     | 
    
         
            +
              def initialize(range)
         
     | 
| 
      
 10 
     | 
    
         
            +
                @range = range
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 14 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                "AVERAGE(#{@range.format(on_sheet: on_sheet)})"
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Choose < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(choice_index: Zaxcel::Cell::ValueType, choices: T::Array[Zaxcel::Cell::ValueType]).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(choice_index, choices:)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @choice_index = choice_index
         
     | 
| 
      
 10 
     | 
    
         
            +
                @choices = choices
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 14 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                args = [Zaxcel::Cell.format(@choice_index, on_sheet: on_sheet)]
         
     | 
| 
      
 16 
     | 
    
         
            +
                args += @choices.map { |choice| Zaxcel::Cell.format(choice, on_sheet: on_sheet) }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                "CHOOSE(#{args.join(',')})"
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Concatenate < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(values: T::Array[Zaxcel::Cell::ValueType]).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(values)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @values = values
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                formatted_values = @values.map do |value|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Zaxcel::Cell.format(value, on_sheet: on_sheet)
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                "CONCATENATE(#{formatted_values.join(',')})"
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::If < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              # access to 'clauses' can be helpful if debugging
         
     | 
| 
      
 8 
     | 
    
         
            +
              sig { returns(Zaxcel::Cell::ValueType) }
         
     | 
| 
      
 9 
     | 
    
         
            +
              attr_reader :condition
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              sig { returns(Zaxcel::Cell::ValueType) }
         
     | 
| 
      
 12 
     | 
    
         
            +
              attr_reader :if_true
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              sig { returns(Zaxcel::Cell::ValueType) }
         
     | 
| 
      
 15 
     | 
    
         
            +
              attr_reader :if_false
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              sig do
         
     | 
| 
      
 18 
     | 
    
         
            +
                params(
         
     | 
| 
      
 19 
     | 
    
         
            +
                  condition: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 20 
     | 
    
         
            +
                  if_true: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if_false: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 22 
     | 
    
         
            +
                ).void
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
              def initialize(condition, if_true:, if_false:)
         
     | 
| 
      
 25 
     | 
    
         
            +
                @condition = condition
         
     | 
| 
      
 26 
     | 
    
         
            +
                @if_true = if_true
         
     | 
| 
      
 27 
     | 
    
         
            +
                @if_false = if_false
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 31 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 32 
     | 
    
         
            +
                formatted_condition = Zaxcel::Cell.format(@condition, on_sheet: on_sheet)
         
     | 
| 
      
 33 
     | 
    
         
            +
                formatted_if_true = Zaxcel::Cell.format(@if_true, on_sheet: on_sheet)
         
     | 
| 
      
 34 
     | 
    
         
            +
                formatted_if_false = Zaxcel::Cell.format(@if_false, on_sheet: on_sheet)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                "IF(#{formatted_condition},#{formatted_if_true},#{formatted_if_false})"
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::IfError < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig do
         
     | 
| 
      
 8 
     | 
    
         
            +
                params(
         
     | 
| 
      
 9 
     | 
    
         
            +
                  value: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  default_value: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 11 
     | 
    
         
            +
                ).void
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
              def initialize(value, default_value:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 15 
     | 
    
         
            +
                @default_value = default_value
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 19 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 20 
     | 
    
         
            +
                formatted_value = Zaxcel::Cell.format(@value, on_sheet: on_sheet)
         
     | 
| 
      
 21 
     | 
    
         
            +
                formatted_default = Zaxcel::Cell.format(@default_value, on_sheet: on_sheet)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                "IFERROR(#{formatted_value},#{formatted_default})"
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Index < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              sig do
         
     | 
| 
      
 6 
     | 
    
         
            +
                params(
         
     | 
| 
      
 7 
     | 
    
         
            +
                  index_value: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  range: Zaxcel::References::Range,
         
     | 
| 
      
 9 
     | 
    
         
            +
                ).void
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
              def initialize(index_value:, range:)
         
     | 
| 
      
 12 
     | 
    
         
            +
                @index_value = index_value
         
     | 
| 
      
 13 
     | 
    
         
            +
                @range = range
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 17 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 18 
     | 
    
         
            +
                "INDEX(#{@range.format(on_sheet: on_sheet)},#{Zaxcel::Cell.format(@index_value, on_sheet: on_sheet)})"
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Len < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(value: Zaxcel::Cell::ValueType).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(value)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                "LEN(#{Zaxcel::Cell.format(@value, on_sheet: on_sheet)})"
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Match::MatchType < T::Enum
         
     | 
| 
      
 5 
     | 
    
         
            +
              enums do
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Finds the first value exactly equal to lookup_value. lookup_array can be in any order.
         
     | 
| 
      
 7 
     | 
    
         
            +
                EXACT = new('0')
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Finds the largest value less than or equal to lookup_value. Requires lookup_array to be in ascending order.
         
     | 
| 
      
 9 
     | 
    
         
            +
                LESS_THAN_OR_EQUAL = new('1')
         
     | 
| 
      
 10 
     | 
    
         
            +
                # Finds the smallest value greater than or equal to lookup_value. Requires lookup_array to be in descending order.
         
     | 
| 
      
 11 
     | 
    
         
            +
                GREATER_THAN_OR_EQUAL = new('-1')
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Match < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              sig do
         
     | 
| 
      
 6 
     | 
    
         
            +
                params(
         
     | 
| 
      
 7 
     | 
    
         
            +
                  value: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  range: Zaxcel::References::Range,
         
     | 
| 
      
 9 
     | 
    
         
            +
                  match_type: T.nilable(Zaxcel::Functions::Match::MatchType),
         
     | 
| 
      
 10 
     | 
    
         
            +
                ).void
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
              def initialize(value:, range:, match_type: nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 14 
     | 
    
         
            +
                @range = range
         
     | 
| 
      
 15 
     | 
    
         
            +
                @match_type = match_type
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 19 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 20 
     | 
    
         
            +
                args = [
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Zaxcel::Cell.format(@value, on_sheet: on_sheet),
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @range.format(on_sheet: on_sheet),
         
     | 
| 
      
 23 
     | 
    
         
            +
                ]
         
     | 
| 
      
 24 
     | 
    
         
            +
                args << @match_type.serialize if @match_type.present?
         
     | 
| 
      
 25 
     | 
    
         
            +
                "MATCH(#{args.join(',')})"
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Max < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(values: T::Array[T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)]).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(values)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @values = values
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                formatted_values = @values.map { |value| Zaxcel::Cell.format(value, on_sheet: on_sheet) }.compact
         
     | 
| 
      
 15 
     | 
    
         
            +
                "MAX(#{formatted_values.join(',')})"
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Min < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(values: T::Array[T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)]).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(values)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @values = values
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                formatted_values = @values.map { |value| Zaxcel::Cell.format(value, on_sheet: on_sheet) }.compact
         
     | 
| 
      
 15 
     | 
    
         
            +
                "MIN(#{formatted_values.join(',')})"
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # This isn't really a function, it's a unary operator, so this is in the wrong spot. But I'm not sure where the best
         
     | 
| 
      
 5 
     | 
    
         
            +
            # place for it is yet, so just leave it here for now.
         
     | 
| 
      
 6 
     | 
    
         
            +
            class Zaxcel::Functions::Negate < Zaxcel::CellFormula
         
     | 
| 
      
 7 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              sig { params(value: Zaxcel::Cell::ValueType).void }
         
     | 
| 
      
 10 
     | 
    
         
            +
              def initialize(value)
         
     | 
| 
      
 11 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 15 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 16 
     | 
    
         
            +
                formatted_value = Zaxcel::Cell.format(@value, on_sheet: on_sheet)
         
     | 
| 
      
 17 
     | 
    
         
            +
                return '0' if formatted_value.nil? && @value.is_a?(Zaxcel::References::Cell)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                formatted_value = "(#{formatted_value})" if @value.is_a?(Zaxcel::BinaryExpression) && [
         
     | 
| 
      
 20 
     | 
    
         
            +
                  '-',
         
     | 
| 
      
 21 
     | 
    
         
            +
                  '+',
         
     | 
| 
      
 22 
     | 
    
         
            +
                ].include?(@value.operator)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                "-#{formatted_value}"
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Or < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              # todo - likely and can accept more than one arg, but not sure. check into this.
         
     | 
| 
      
 8 
     | 
    
         
            +
              # also, it may accept only specific types of formulas (returns true or false or something), but my suspicion is that
         
     | 
| 
      
 9 
     | 
    
         
            +
              # is not true. confirm this also, since right now it's typed to a strange type.
         
     | 
| 
      
 10 
     | 
    
         
            +
              sig { params(left_hand_value: Zaxcel::Cell::ValueType, right_hand_value: Zaxcel::Cell::ValueType).void }
         
     | 
| 
      
 11 
     | 
    
         
            +
              def initialize(left_hand_value, right_hand_value)
         
     | 
| 
      
 12 
     | 
    
         
            +
                @left_hand_value = left_hand_value
         
     | 
| 
      
 13 
     | 
    
         
            +
                @right_hand_value = right_hand_value
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 17 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 18 
     | 
    
         
            +
                left_hand_formatted_value = Zaxcel::Cell.format(@left_hand_value, on_sheet: on_sheet)
         
     | 
| 
      
 19 
     | 
    
         
            +
                right_hand_formatted_value = Zaxcel::Cell.format(@right_hand_value, on_sheet: on_sheet)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                "OR(#{left_hand_formatted_value},#{right_hand_formatted_value})"
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Round < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(value: Zaxcel::Cell::ValueType, precision: Integer).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(value, precision: 0)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 10 
     | 
    
         
            +
                @precision = precision
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 14 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                formatted_value = Zaxcel::Cell.format(@value, on_sheet: on_sheet)
         
     | 
| 
      
 16 
     | 
    
         
            +
                return '0' if formatted_value.nil? && @value.is_a?(Zaxcel::References::Cell)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                "ROUND(#{formatted_value},#{@precision})"
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Sum < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(values: T::Array[T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)]).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(values)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @values = values
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                return '0' if @values.blank?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                "SUM(#{@values.map { |value| Zaxcel::Cell.format(value, on_sheet: on_sheet) }.join(',')})"
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::SumIf < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(range_to_check: Zaxcel::References::Range, value_to_check: Zaxcel::Cell::ValueType, range_to_sum: Zaxcel::References::Range).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(range_to_check:, value_to_check:, range_to_sum:)
         
     | 
| 
      
 9 
     | 
    
         
            +
                raise 'Column to check and column to sum must be on the same sheet!' if range_to_check.sheet_name != range_to_sum.sheet_name
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                @range_to_check = range_to_check
         
     | 
| 
      
 12 
     | 
    
         
            +
                @value_to_check = value_to_check
         
     | 
| 
      
 13 
     | 
    
         
            +
                @range_to_sum = range_to_sum
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 17 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 18 
     | 
    
         
            +
                "SUMIF(#{@range_to_check.format(on_sheet: on_sheet)},#{Zaxcel::Cell.format(@value_to_check, on_sheet: on_sheet)},#{@range_to_sum.format(on_sheet: on_sheet)})"
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::SumIfs < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig do
         
     | 
| 
      
 8 
     | 
    
         
            +
                params(
         
     | 
| 
      
 9 
     | 
    
         
            +
                  ranges_to_check: T::Array[Zaxcel::References::Range],
         
     | 
| 
      
 10 
     | 
    
         
            +
                  values_to_check: T::Array[Zaxcel::Cell::ValueType],
         
     | 
| 
      
 11 
     | 
    
         
            +
                  range_to_sum: Zaxcel::References::Range,
         
     | 
| 
      
 12 
     | 
    
         
            +
                ).void
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
              def initialize(ranges_to_check:, values_to_check:, range_to_sum:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                raise 'Columns to check and values to check must be the same length' if ranges_to_check.count != values_to_check.count
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                @ranges_to_check = ranges_to_check
         
     | 
| 
      
 18 
     | 
    
         
            +
                @values_to_check = values_to_check
         
     | 
| 
      
 19 
     | 
    
         
            +
                @range_to_sum = range_to_sum
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 23 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 24 
     | 
    
         
            +
                criteria = @ranges_to_check.flat_map.with_index do |check_range, index|
         
     | 
| 
      
 25 
     | 
    
         
            +
                  check_value = @values_to_check[index]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  [
         
     | 
| 
      
 27 
     | 
    
         
            +
                    check_range.format(on_sheet: on_sheet),
         
     | 
| 
      
 28 
     | 
    
         
            +
                    Zaxcel::Cell.format(check_value, on_sheet: on_sheet, quote_strings: false),
         
     | 
| 
      
 29 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                "SUMIFS(#{@range_to_sum.format(on_sheet: on_sheet)},#{criteria.join(',')})"
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::SumProduct < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(values: T::Array[T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)]).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(values)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @values = values
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 13 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 14 
     | 
    
         
            +
                return '0' if @values.blank?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                "SUMPRODUCT(#{@values.map { |value| Zaxcel::Cell.format(value, on_sheet: on_sheet) }.join(',')})"
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Text < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(value: Zaxcel::Cell::ValueType, format_string: String).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(value, format_string:)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @value = value
         
     | 
| 
      
 10 
     | 
    
         
            +
                @format_string = format_string
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 14 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                "TEXT(#{Zaxcel::Cell.format(@value, on_sheet: on_sheet)},#{Zaxcel::Cell.format(@format_string, on_sheet: on_sheet)})"
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::Unique < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig { params(range: Zaxcel::References::Range, array_formula: T::Boolean).void }
         
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(range, array_formula: false)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @range = range
         
     | 
| 
      
 10 
     | 
    
         
            +
                @array_formula = array_formula
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 14 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                "UNIQUE(#{@range.format(on_sheet: on_sheet)})"
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # We'll probably want to abstract this into an interface or common class or something, but for now just define here
         
     | 
| 
      
 19 
     | 
    
         
            +
              sig { returns(T::Boolean) }
         
     | 
| 
      
 20 
     | 
    
         
            +
              def array_formula?
         
     | 
| 
      
 21 
     | 
    
         
            +
                @array_formula
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: strict
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class Zaxcel::Functions::XLookup < Zaxcel::Function
         
     | 
| 
      
 5 
     | 
    
         
            +
              extend T::Sig
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              sig do
         
     | 
| 
      
 8 
     | 
    
         
            +
                params(
         
     | 
| 
      
 9 
     | 
    
         
            +
                  condition: Zaxcel::Cell::ValueType,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  idx_range: Zaxcel::References::Range,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  value_range: Zaxcel::References::Range,
         
     | 
| 
      
 12 
     | 
    
         
            +
                ).void
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
              def initialize(condition, idx_range:, value_range:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                @condition = condition
         
     | 
| 
      
 16 
     | 
    
         
            +
                @idx_range = idx_range
         
     | 
| 
      
 17 
     | 
    
         
            +
                @value_range = value_range
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              sig { override.params(on_sheet: String).returns(String) }
         
     | 
| 
      
 21 
     | 
    
         
            +
              def format(on_sheet:)
         
     | 
| 
      
 22 
     | 
    
         
            +
                formatted_condition = Zaxcel::Cell.format(@condition, on_sheet: on_sheet)
         
     | 
| 
      
 23 
     | 
    
         
            +
                formatted_idx_range = @idx_range.format(on_sheet: on_sheet)
         
     | 
| 
      
 24 
     | 
    
         
            +
                formatted_value_range = @value_range.format(on_sheet: on_sheet)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                "XLOOKUP(#{formatted_condition},#{formatted_idx_range},#{formatted_value_range})"
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     |