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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +29 -0
  5. data/CONTRIBUTING.md +110 -0
  6. data/LICENSE +22 -0
  7. data/QUICK_START.md +187 -0
  8. data/README.md +372 -0
  9. data/Rakefile +18 -0
  10. data/SETUP.md +178 -0
  11. data/lib/enumerable.rb +47 -0
  12. data/lib/zaxcel/README.md +37 -0
  13. data/lib/zaxcel/arithmetic.rb +88 -0
  14. data/lib/zaxcel/binary_expression.rb +74 -0
  15. data/lib/zaxcel/binary_expressions/addition.rb +36 -0
  16. data/lib/zaxcel/binary_expressions/division.rb +24 -0
  17. data/lib/zaxcel/binary_expressions/multiplication.rb +24 -0
  18. data/lib/zaxcel/binary_expressions/subtraction.rb +41 -0
  19. data/lib/zaxcel/binary_expressions.rb +38 -0
  20. data/lib/zaxcel/cell.rb +141 -0
  21. data/lib/zaxcel/cell_formula.rb +16 -0
  22. data/lib/zaxcel/column.rb +142 -0
  23. data/lib/zaxcel/document.rb +136 -0
  24. data/lib/zaxcel/function.rb +6 -0
  25. data/lib/zaxcel/functions/abs.rb +18 -0
  26. data/lib/zaxcel/functions/and.rb +23 -0
  27. data/lib/zaxcel/functions/average.rb +17 -0
  28. data/lib/zaxcel/functions/choose.rb +20 -0
  29. data/lib/zaxcel/functions/concatenate.rb +20 -0
  30. data/lib/zaxcel/functions/if.rb +38 -0
  31. data/lib/zaxcel/functions/if_error.rb +25 -0
  32. data/lib/zaxcel/functions/index.rb +20 -0
  33. data/lib/zaxcel/functions/len.rb +16 -0
  34. data/lib/zaxcel/functions/match/match_type.rb +13 -0
  35. data/lib/zaxcel/functions/match.rb +27 -0
  36. data/lib/zaxcel/functions/max.rb +17 -0
  37. data/lib/zaxcel/functions/min.rb +17 -0
  38. data/lib/zaxcel/functions/negate.rb +26 -0
  39. data/lib/zaxcel/functions/or.rb +23 -0
  40. data/lib/zaxcel/functions/round.rb +20 -0
  41. data/lib/zaxcel/functions/sum.rb +18 -0
  42. data/lib/zaxcel/functions/sum_if.rb +20 -0
  43. data/lib/zaxcel/functions/sum_ifs.rb +34 -0
  44. data/lib/zaxcel/functions/sum_product.rb +18 -0
  45. data/lib/zaxcel/functions/text.rb +17 -0
  46. data/lib/zaxcel/functions/unique.rb +23 -0
  47. data/lib/zaxcel/functions/x_lookup.rb +28 -0
  48. data/lib/zaxcel/functions/xirr.rb +27 -0
  49. data/lib/zaxcel/functions.rb +169 -0
  50. data/lib/zaxcel/if_builder.rb +22 -0
  51. data/lib/zaxcel/lang.rb +23 -0
  52. data/lib/zaxcel/reference.rb +28 -0
  53. data/lib/zaxcel/references/cell.rb +42 -0
  54. data/lib/zaxcel/references/column.rb +49 -0
  55. data/lib/zaxcel/references/range.rb +35 -0
  56. data/lib/zaxcel/references/row.rb +34 -0
  57. data/lib/zaxcel/references.rb +5 -0
  58. data/lib/zaxcel/roundable.rb +14 -0
  59. data/lib/zaxcel/row.rb +93 -0
  60. data/lib/zaxcel/sheet.rb +425 -0
  61. data/lib/zaxcel/sorbet/enumerizable_enum.rb +50 -0
  62. data/lib/zaxcel/version.rb +6 -0
  63. data/lib/zaxcel.rb +73 -0
  64. data/zaxcel.gemspec +73 -0
  65. metadata +266 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ # https://support.microsoft.com/en-us/office/xirr-function-de1242ec-6477-445b-b11b-a303ad9adc9d
5
+ class Zaxcel::Functions::Xirr < Zaxcel::Function
6
+ extend T::Sig
7
+
8
+ sig do
9
+ params(
10
+ values: Zaxcel::References::Range,
11
+ dates: Zaxcel::References::Range,
12
+ guess: T.nilable(Zaxcel::CellFormula),
13
+ ).void
14
+ end
15
+ def initialize(values, dates, guess: nil)
16
+ @values = values
17
+ @dates = dates
18
+ @guess = guess
19
+ end
20
+
21
+ sig { override.params(on_sheet: String).returns(String) }
22
+ def format(on_sheet:)
23
+ guess_string = ", #{Zaxcel::Cell.format(@guess, on_sheet: on_sheet)}" if @guess.present?
24
+
25
+ "XIRR(#{@values.format(on_sheet: on_sheet)},#{@dates.format(on_sheet: on_sheet)}#{guess_string})"
26
+ end
27
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module Zaxcel::Functions
5
+ class << self
6
+ extend T::Sig
7
+
8
+ sig { params(value: Zaxcel::Cell::ValueType).returns(Zaxcel::Functions::Abs) }
9
+ def abs(value)
10
+ Zaxcel::Functions::Abs.new(value)
11
+ end
12
+
13
+ sig do
14
+ params(
15
+ lh_value: Zaxcel::Cell::ValueType,
16
+ rh_value: Zaxcel::Cell::ValueType,
17
+ ).returns(Zaxcel::Functions::And)
18
+ end
19
+ def and(lh_value, rh_value)
20
+ Zaxcel::Functions::And.new(lh_value, rh_value)
21
+ end
22
+
23
+ sig { params(range: Zaxcel::References::Range).returns(Zaxcel::Functions::Average) }
24
+ def average(range)
25
+ Zaxcel::Functions::Average.new(range)
26
+ end
27
+
28
+ sig { params(values: Zaxcel::Cell::ValueType).returns(Zaxcel::Functions::Concatenate) }
29
+ def concatenate(*values)
30
+ Zaxcel::Functions::Concatenate.new(values)
31
+ end
32
+
33
+ sig do
34
+ params(
35
+ choice_index: Zaxcel::Cell::ValueType,
36
+ choices: T::Array[Zaxcel::Cell::ValueType],
37
+ ).returns(Zaxcel::Functions::Choose)
38
+ end
39
+ def choose(choice_index, choices:)
40
+ Zaxcel::Functions::Choose.new(choice_index, choices: choices)
41
+ end
42
+
43
+ sig do
44
+ params(
45
+ value: Zaxcel::Cell::ValueType,
46
+ default_value: Zaxcel::Cell::ValueType,
47
+ ).returns(Zaxcel::Functions::IfError)
48
+ end
49
+ def if_error(value, default_value:)
50
+ Zaxcel::Functions::IfError.new(value, default_value: default_value)
51
+ end
52
+
53
+ sig do
54
+ params(
55
+ index_value: Zaxcel::Cell::ValueType,
56
+ range: Zaxcel::References::Column,
57
+ ).returns(Zaxcel::Functions::Index)
58
+ end
59
+ def index(index_value:, range:)
60
+ Zaxcel::Functions::Index.new(index_value: index_value, range: Zaxcel::Lang.range(range))
61
+ end
62
+
63
+ sig { params(value: Zaxcel::Cell::ValueType, range: Zaxcel::References::Column, match_type: T.nilable(Zaxcel::Functions::Match::MatchType)).returns(Zaxcel::Functions::Match) }
64
+ def match(value:, range:, match_type: nil)
65
+ Zaxcel::Functions::Match.new(value: value, range: Zaxcel::Lang.range(range), match_type: match_type)
66
+ end
67
+
68
+ sig { params(values: T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)).returns(Zaxcel::Functions::Max) }
69
+ def max(*values)
70
+ Zaxcel::Functions::Max.new(values)
71
+ end
72
+
73
+ sig { params(values: T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)).returns(Zaxcel::Functions::Min) }
74
+ def min(*values)
75
+ Zaxcel::Functions::Min.new(values)
76
+ end
77
+
78
+ sig do
79
+ params(
80
+ lh_value: Zaxcel::Cell::ValueType,
81
+ rh_value: Zaxcel::Cell::ValueType,
82
+ ).returns(Zaxcel::Functions::Or)
83
+ end
84
+ def or(lh_value, rh_value)
85
+ Zaxcel::Functions::Or.new(lh_value, rh_value)
86
+ end
87
+
88
+ sig { params(value: Zaxcel::Cell::ValueType, precision: Integer).returns(Zaxcel::Functions::Round) }
89
+ def round(value, precision: 0)
90
+ Zaxcel::Functions::Round.new(value, precision: precision)
91
+ end
92
+
93
+ sig { params(values: T.any(Zaxcel::Cell::ValueType, Zaxcel::References::Range)).returns(Zaxcel::Functions::Sum) }
94
+ def sum(*values)
95
+ Zaxcel::Functions::Sum.new(values)
96
+ end
97
+
98
+ sig do
99
+ params(
100
+ column_to_check: Zaxcel::References::Column,
101
+ value_to_check: Zaxcel::Cell::ValueType,
102
+ column_to_sum: Zaxcel::References::Column,
103
+ ).returns(Zaxcel::Functions::SumIf)
104
+ end
105
+ def sum_if(column_to_check:, value_to_check:, column_to_sum:)
106
+ Zaxcel::Functions::SumIf.new(
107
+ range_to_check: Zaxcel::Lang.range(column_to_check),
108
+ value_to_check: value_to_check,
109
+ range_to_sum: Zaxcel::Lang.range(column_to_sum),
110
+ )
111
+ end
112
+
113
+ sig do
114
+ params(
115
+ ranges_to_check: T::Array[Zaxcel::References::Range],
116
+ values_to_check: T::Array[Zaxcel::Cell::ValueType],
117
+ range_to_sum: Zaxcel::References::Range,
118
+ ).returns(Zaxcel::Functions::SumIfs)
119
+ end
120
+ def sum_ifs(ranges_to_check:, values_to_check:, range_to_sum:)
121
+ Zaxcel::Functions::SumIfs.new(
122
+ ranges_to_check: ranges_to_check,
123
+ values_to_check: values_to_check,
124
+ range_to_sum: range_to_sum,
125
+ )
126
+ end
127
+
128
+ sig { params(range_values: T::Enumerable[Zaxcel::References::Range::RangeableType]).returns(Zaxcel::Functions::Sum) }
129
+ def sum_range(range_values)
130
+ range = Zaxcel::Lang.range(range_values.first!, range_values.last!) if range_values.any?
131
+ Zaxcel::Functions::Sum.new([range].compact)
132
+ end
133
+
134
+ sig { params(value: Zaxcel::Cell::ValueType, format_string: String).returns(Zaxcel::Functions::Text) }
135
+ def text(value, format_string:)
136
+ Zaxcel::Functions::Text.new(value, format_string: format_string)
137
+ end
138
+
139
+ sig { params(value: Zaxcel::Cell::ValueType).returns(Zaxcel::Functions::Len) }
140
+ def len(value)
141
+ Zaxcel::Functions::Len.new(value)
142
+ end
143
+
144
+ sig do
145
+ params(
146
+ condition: Zaxcel::Cell::ValueType,
147
+ idx_range: Zaxcel::References::Range,
148
+ value_range: Zaxcel::References::Range,
149
+ ).returns(Zaxcel::Functions::XLookup)
150
+ end
151
+ def x_lookup(condition, idx_range:, value_range:)
152
+ Zaxcel::Functions::XLookup.new(condition, idx_range: idx_range, value_range: value_range)
153
+ end
154
+
155
+ sig do
156
+ params(
157
+ values: T::Array[Zaxcel::References::Cell],
158
+ dates: T::Array[Zaxcel::References::Cell],
159
+ guess: T.nilable(Zaxcel::CellFormula),
160
+ ).returns(Zaxcel::Functions::Xirr)
161
+ end
162
+ def xirr(values:, dates:, guess: nil)
163
+ value_range = Zaxcel::References::Range.new(values.first!, values.last!)
164
+ date_range = Zaxcel::References::Range.new(dates.first!, dates.last!)
165
+
166
+ Zaxcel::Functions::Xirr.new(value_range, date_range, guess: guess)
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::IfBuilder
5
+ extend T::Sig
6
+
7
+ sig { params(condition: Zaxcel::Cell::ValueType).void }
8
+ def initialize(condition)
9
+ @condition = T.let(condition, Zaxcel::Cell::ValueType)
10
+ end
11
+
12
+ sig { params(result: Zaxcel::Cell::ValueType).returns(Zaxcel::IfBuilder) }
13
+ def then(result)
14
+ @then = T.let(result, Zaxcel::Cell::ValueType)
15
+ self
16
+ end
17
+
18
+ sig { params(result: Zaxcel::Cell::ValueType).returns(Zaxcel::Functions::If) }
19
+ def else(result)
20
+ Zaxcel::Functions::If.new(@condition, if_true: @then, if_false: result)
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module Zaxcel::Lang
5
+ class << self
6
+ extend T::Sig
7
+
8
+ sig { params(condition: Zaxcel::Cell::ValueType).returns(Zaxcel::IfBuilder) }
9
+ def if(condition)
10
+ Zaxcel::IfBuilder.new(condition)
11
+ end
12
+
13
+ sig do
14
+ params(
15
+ lh_value: Zaxcel::References::Range::RangeableType,
16
+ rh_value: T.nilable(Zaxcel::References::Range::RangeableType),
17
+ ).returns(Zaxcel::References::Range)
18
+ end
19
+ def range(lh_value, rh_value = nil)
20
+ Zaxcel::References::Range.new(lh_value, rh_value)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::Reference
5
+ extend T::Sig
6
+ extend T::Helpers
7
+
8
+ abstract!
9
+
10
+ sig { abstract.returns(String) }
11
+ def sheet_name; end
12
+
13
+ sig { abstract.returns(String) }
14
+ def excel_sheet_name; end
15
+
16
+ sig { abstract.returns(String) }
17
+ def resolve; end
18
+
19
+ sig { params(on_sheet: String).returns(T.nilable(String)) }
20
+ def format(on_sheet:)
21
+ formatted_string = resolve
22
+ return if formatted_string.blank?
23
+
24
+ formatted_string = "'#{excel_sheet_name}'!#{formatted_string}" if on_sheet.present? && on_sheet != sheet_name
25
+
26
+ formatted_string
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::References::Cell < Zaxcel::Reference
5
+ include Zaxcel::Arithmetic
6
+ include Zaxcel::Roundable
7
+ extend T::Sig
8
+
9
+ sig { override.returns(String) }
10
+ attr_reader :sheet_name
11
+
12
+ sig { returns(Zaxcel::Document) }
13
+ attr_reader :document
14
+
15
+ sig { returns(Symbol) }
16
+ attr_reader :column_name, :row_name
17
+
18
+ sig { params(document: Zaxcel::Document, sheet_name: String, row_name: Symbol, col_name: Symbol).void }
19
+ def initialize(document:, sheet_name:, row_name:, col_name:)
20
+ super()
21
+
22
+ @document = document
23
+ @sheet_name = sheet_name
24
+ @column_name = col_name
25
+ @row_name = row_name
26
+ end
27
+
28
+ sig { override.returns(String) }
29
+ def resolve
30
+ cell&.to_excel || ''
31
+ end
32
+
33
+ sig { override.returns(String) }
34
+ def excel_sheet_name
35
+ T.must(@document.sheet_by_name[@sheet_name]&.to_excel)
36
+ end
37
+
38
+ sig { returns(T.nilable(Zaxcel::Cell)) }
39
+ def cell
40
+ @document.sheet(@sheet_name)&.column(@column_name)&.cell(@row_name)
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::References::Column < Zaxcel::Reference
5
+ extend T::Sig
6
+
7
+ sig { override.returns(String) }
8
+ attr_reader :sheet_name
9
+
10
+ sig { params(sheet_name: String, col_name: Symbol, document: Zaxcel::Document).void }
11
+ def initialize(sheet_name:, col_name:, document:)
12
+ super()
13
+
14
+ @sheet_name = sheet_name
15
+ @col_name = col_name
16
+ @document = document
17
+ end
18
+
19
+ sig { override.returns(String) }
20
+ def resolve
21
+ T.must_because(column) { "No column on sheet #{sheet_name} named #{@col_name}" }
22
+ .to_excel
23
+ end
24
+
25
+ sig { returns(T.nilable(Zaxcel::Column)) }
26
+ def column
27
+ @column ||= T.let(@document.sheet(@sheet_name)&.column(@col_name), T.nilable(Zaxcel::Column))
28
+ end
29
+
30
+ sig { override.returns(String) }
31
+ def excel_sheet_name
32
+ T.must(@document.sheet_by_name[@sheet_name]&.to_excel)
33
+ end
34
+
35
+ sig { returns(T::Array[Zaxcel::Cell]) }
36
+ def cells
37
+ return [] if column.nil?
38
+
39
+ @cells ||= T.let(
40
+ begin
41
+ cells_in_column = T.must(column).cell_by_row_name.values
42
+ raise 'Must position cells before resolving references.' if cells_in_column.any? { |cell| cell.col_position.nil? }
43
+
44
+ cells_in_column.sort_by { |cell| T.must(cell.row_position) }
45
+ end,
46
+ T.nilable(T::Array[Zaxcel::Cell]),
47
+ )
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::References::Range < Zaxcel::Reference
5
+ extend T::Sig
6
+
7
+ RangeableType = T.type_alias { T.any(Zaxcel::References::Cell, Zaxcel::References::Row, Zaxcel::References::Column) }
8
+
9
+ sig { params(lh_value: RangeableType, rh_value: T.nilable(RangeableType)).void }
10
+ def initialize(lh_value, rh_value)
11
+ super()
12
+
13
+ rh_value ||= lh_value
14
+ raise 'Ranges must be from cells on the same sheet' if lh_value.sheet_name != rh_value.sheet_name
15
+ raise "Incompatible range values #{lh_value.class.name}:#{rh_value.class.name}" if lh_value.class != rh_value.class
16
+
17
+ @lh_value = lh_value
18
+ @rh_value = T.let(rh_value, RangeableType)
19
+ end
20
+
21
+ sig { override.returns(String) }
22
+ def resolve
23
+ "#{@lh_value.resolve}:#{@rh_value.resolve}"
24
+ end
25
+
26
+ sig { override.returns(String) }
27
+ def excel_sheet_name
28
+ @lh_value.excel_sheet_name
29
+ end
30
+
31
+ sig { override.returns(String) }
32
+ def sheet_name
33
+ @lh_value.sheet_name
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::References::Row < Zaxcel::Reference
5
+ extend T::Sig
6
+
7
+ sig { override.returns(String) }
8
+ attr_reader :sheet_name
9
+
10
+ sig { params(sheet_name: String, row_name: Symbol, document: Zaxcel::Document).void }
11
+ def initialize(sheet_name:, row_name:, document:)
12
+ super()
13
+
14
+ @sheet_name = sheet_name
15
+ @row_name = row_name
16
+ @document = document
17
+ end
18
+
19
+ sig { override.returns(String) }
20
+ def resolve
21
+ T.must_because(row) { "No row on sheet #{sheet_name} named #{@row_name}" }
22
+ .to_excel
23
+ end
24
+
25
+ sig { override.returns(String) }
26
+ def excel_sheet_name
27
+ T.must(@document.sheet_by_name[@sheet_name]&.to_excel)
28
+ end
29
+
30
+ sig { returns(T.nilable(Zaxcel::Row)) }
31
+ def row
32
+ @row ||= T.let(@document.sheet(sheet_name)&.row(@row_name), T.nilable(Zaxcel::Row))
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module Zaxcel::References
5
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module Zaxcel::Roundable
5
+ extend T::Sig
6
+ extend T::Helpers
7
+
8
+ requires_ancestor { Zaxcel::Arithmetic }
9
+
10
+ sig { params(precision: Integer).returns(Zaxcel::Functions::Round) }
11
+ def round(precision: 0)
12
+ Zaxcel::Functions::Round.new(T.bind(self, Zaxcel::Arithmetic), precision: precision)
13
+ end
14
+ end
data/lib/zaxcel/row.rb ADDED
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ class Zaxcel::Row
5
+ extend T::Sig
6
+
7
+ sig { returns(Symbol) }
8
+ attr_reader :name, :style_group
9
+
10
+ sig { returns(T.nilable(Integer)) }
11
+ attr_reader :height
12
+
13
+ sig { returns(T.nilable(Integer)) }
14
+ attr_reader :position
15
+
16
+ sig { params(row_name: Symbol, sheet: Zaxcel::Sheet, style_group: Symbol, height: T.nilable(Integer), hidden: T::Boolean).void }
17
+ def initialize(row_name, sheet:, style_group: :row_style, height: nil, hidden: false)
18
+ @name = row_name
19
+ @sheet = sheet
20
+ @style_group = style_group
21
+ @height = height
22
+ @print_boundary = T.let(false, T::Boolean)
23
+ @hidden = hidden
24
+ end
25
+
26
+ sig { params(col_name: T.any(String, Symbol), sheet_name: T.nilable(String)).returns(Zaxcel::References::Cell) }
27
+ def ref(col_name, sheet_name: nil)
28
+ Zaxcel::References::Cell.new(document: @sheet.document, sheet_name: sheet_name || @sheet.name, col_name: col_name.to_sym, row_name: @name)
29
+ end
30
+
31
+ sig { params(column_name: T.any(Symbol, String), value: Zaxcel::Cell::ValueType, style: T.nilable(Symbol), to_extract: T::Boolean).returns(Zaxcel::Row) }
32
+ def add!(column_name, value:, style: nil, to_extract: false)
33
+ column = @sheet.column(column_name)
34
+ raise "Cannot add a cell to a non-existent column #{column_name}" if column.nil?
35
+
36
+ cell_by_column_name[column_name.to_sym] = column.add_cell!(value: value, row: self, style: style, to_extract: to_extract)
37
+
38
+ self
39
+ end
40
+
41
+ sig { params(col_name: T.any(Symbol, String)).void }
42
+ def add_empty!(col_name)
43
+ add!(col_name, value: Zaxcel::Cell::EMPTY_VALUE)
44
+ end
45
+
46
+ sig { params(cell_value_hash: T::Hash[Symbol, Zaxcel::Cell::ValueType]).returns(Zaxcel::Row) }
47
+ def add_many!(cell_value_hash)
48
+ cell_value_hash.each do |col_name, value|
49
+ add!(col_name.to_sym, value: value)
50
+ end
51
+
52
+ self
53
+ end
54
+
55
+ sig { params(column_name: T.any(Symbol, String)).returns(T.nilable(Zaxcel::Cell)) }
56
+ def cell(column_name)
57
+ cell_by_column_name[column_name.to_sym]
58
+ end
59
+
60
+ sig { params(row_position: Integer).void }
61
+ def position!(row_position)
62
+ @position = T.let(row_position, T.nilable(Integer))
63
+ end
64
+
65
+ sig { returns(T::Hash[Symbol, Zaxcel::Cell]) }
66
+ def cell_by_column_name
67
+ @cell_by_column_name ||= T.let({}, T.nilable(T::Hash[Symbol, Zaxcel::Cell]))
68
+ end
69
+
70
+ sig { returns(String) }
71
+ def to_excel
72
+ raise 'Must position cells before calling to_excel' if @position.nil?
73
+
74
+ @position.to_s
75
+ end
76
+
77
+ sig { void }
78
+ def set_print_boundary!
79
+ raise 'Print boundary row already exists' if @sheet.print_boundary_row.present?
80
+
81
+ @print_boundary = true
82
+ end
83
+
84
+ sig { returns(T::Boolean) }
85
+ def print_boundary?
86
+ @print_boundary
87
+ end
88
+
89
+ sig { returns(T::Boolean) }
90
+ def hidden?
91
+ @hidden
92
+ end
93
+ end