Class: Xlsxrb::Elements::Cell

Inherits:
Object
  • Object
show all
Defined in:
lib/xlsxrb/elements/cell.rb,
sig/generated/xlsxrb/elements/cell.rbs

Overview

Represents a single cell in a worksheet. All row and column indices are 0-based.

Examples:

Access cell properties

cell = sheet["A1"]
cell.value       # raw value
cell.ref         # "A1"
cell.to_i        # integer value
cell.to_date     # Date value

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(row_index:, column_index:, value: nil, formula: nil, style_index: nil, unmapped_data: EMPTY_HASH, errors: nil) ⇒ Cell

: (row_index: untyped, column_index: untyped, ?value: untyped, ?formula: (Elements::Formula | String)?, ?style_index: (Integer | String)?, ?unmapped_data: Hash[untyped, untyped], ?errors: Array?) -> void

Parameters:

  • row_index (Integer)

    0-based row index.

  • column_index (Integer)

    0-based column index.

  • value (Object, nil) (defaults to: nil)

    The cell's value.

  • formula (Elements::Formula, nil) (defaults to: nil)

    Optional formula.

  • style_index (Integer, String, nil) (defaults to: nil)

    Style identifier.

  • unmapped_data (Hash) (defaults to: EMPTY_HASH)

    Additional metadata.

  • errors (Array<String>, nil) (defaults to: nil)

    Validation errors.

  • row_index: (Object)
  • column_index: (Object)
  • value: (Object) (defaults to: nil)
  • formula: (Elements::Formula, String, nil) (defaults to: nil)
  • style_index: (Integer, String, nil) (defaults to: nil)
  • unmapped_data: (Hash[untyped, untyped]) (defaults to: EMPTY_HASH)
  • errors: (Array[String], nil) (defaults to: nil)


29
30
31
32
33
34
35
36
37
38
# File 'lib/xlsxrb/elements/cell.rb', line 29

def initialize(row_index:, column_index:, value: nil, formula: nil, style_index: nil, unmapped_data: EMPTY_HASH, errors: nil)
  @row_index = row_index
  @column_index = column_index
  @value = value
  @formula = formula
  @style_index = style_index
  @unmapped_data = unmapped_data || EMPTY_HASH
  computed_errors = errors || self.class.validate(row_index, column_index, value)
  @errors = computed_errors.frozen? ? computed_errors : computed_errors.freeze
end

Instance Attribute Details

#column_indexObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def column_index
  @column_index
end

#errorsObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def errors
  @errors
end

#formulaObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def formula
  @formula
end

#row_indexObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def row_index
  @row_index
end

#style_indexObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def style_index
  @style_index
end

#unmapped_dataObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def unmapped_data
  @unmapped_data
end

#valueObject (readonly)

Returns:

  • (Object)


19
20
21
# File 'lib/xlsxrb/elements/cell.rb', line 19

def value
  @value
end

Class Method Details

.column_index(letter) ⇒ Integer

Converts a column letter (e.g. "A", :AA) to a 0-based column index.

: (String | Symbol | Integer letter) -> Integer

Examples:

Cell.column_index("A")  #=> 0
Cell.column_index(:AA)  #=> 26

Parameters:

  • letter (String, Symbol, Integer)

    Column letter or integer index.

Returns:

  • (Integer)

    0-based column index.



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/xlsxrb/elements/cell.rb', line 251

def self.column_index(letter)
  if letter.is_a?(Integer)
    raise ArgumentError, "Column index must be >= 0, got #{letter}" if letter.negative?

    return letter
  end

  str = letter.to_s
  if str.match?(/\A-?\d+\z/)
    val = str.to_i
    raise ArgumentError, "Column index must be >= 0, got #{val}" if val.negative?

    return val
  end

  raise ArgumentError, "Invalid column letter: #{letter.inspect}" unless str.match?(/\A[a-zA-Z]+\z/)

  str.upcase.chars.reduce(0) { |acc, c| (acc * 26) + (c.ord - "A".ord + 1) } - 1
end

.column_letter(index) ⇒ String

Converts a 0-based column index to an Excel letter (0 -> "A", 25 -> "Z", 26 -> "AA").

: (untyped index) -> String

Examples:

Cell.column_letter(0)  #=> "A"
Cell.column_letter(26) #=> "AA"

Parameters:

  • index (Integer)

    0-based column index.

Returns:

  • (String)

    Excel column letter.



226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/xlsxrb/elements/cell.rb', line 226

def self.column_letter(index)
  raise ArgumentError, "Column index must be a non-negative Integer, got #{index.inspect}" unless index.is_a?(Integer) && index >= 0

  @column_letters[index] || begin
    result = +""
    i = index
    loop do
      result.prepend(("A".ord + (i % 26)).chr)
      i = (i / 26) - 1
      break if i.negative?
    end
    result
  end
end

.fast_create(row_index, column_index, value, style_index = nil, formula = nil) ⇒ Object

Parameters:

  • row_index (Object)
  • column_index (Object)
  • value (Object)
  • style_index (Object) (defaults to: nil)
  • formula (Object) (defaults to: nil)

Returns:

  • (Object)


40
41
42
43
44
45
46
47
48
49
50
# File 'lib/xlsxrb/elements/cell.rb', line 40

def self.fast_create(row_index, column_index, value, style_index = nil, formula = nil)
  inst = allocate
  inst.instance_variable_set(:@row_index, row_index)
  inst.instance_variable_set(:@column_index, column_index)
  inst.instance_variable_set(:@value, value)
  inst.instance_variable_set(:@formula, formula)
  inst.instance_variable_set(:@style_index, style_index)
  inst.instance_variable_set(:@unmapped_data, EMPTY_HASH)
  inst.instance_variable_set(:@errors, EMPTY_ERRORS)
  inst
end

.parse_ref(ref) ⇒ Array(Integer, Integer)?

Parses an Excel-style reference to [row_index, col_index] (both 0-based).

: (String? ref) -> [Integer, Integer]?

Examples:

Cell.parse_ref("A1")  #=> [0, 0]
Cell.parse_ref("C10") #=> [9, 2]

Parameters:

  • ref (String, nil)

    Excel cell reference.

Returns:

  • (Array(Integer, Integer), nil)

    0-based [row_index, col_index].



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/xlsxrb/elements/cell.rb', line 281

def self.parse_ref(ref)
  return nil unless ref

  bytes = ref.b
  len = bytes.bytesize
  col = 0
  i = 0
  while i < len
    b = bytes.getbyte(i)
    if b.between?(65, 90)
      col = (col * 26) + (b - 64)
      i += 1
    elsif b.between?(97, 122)
      col = (col * 26) + (b - 96)
      i += 1
    else
      break
    end
  end
  return nil if i.zero? || i == len

  row = bytes.byteslice(i, len - i).to_i - 1
  [row, col - 1]
end

.validate(row_index, column_index, value) ⇒ Array<String>

Validates cell coordinates and value type against OOXML specifications.

: (untyped row_index, untyped column_index, untyped value) -> Array

Parameters:

  • row_index (Integer)
  • column_index (Integer)
  • value (Object)

Returns:

  • (Array<String>)

    List of errors.



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/xlsxrb/elements/cell.rb', line 313

def self.validate(row_index, column_index, value)
  if row_index.is_a?(Integer) && row_index >= 0 && row_index < 1_048_576 &&
     column_index.is_a?(Integer) && column_index >= 0 && column_index < 16_384 &&
     (value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false || value.is_a?(Date) || value.is_a?(Time) || value.is_a?(Formula) || (value.is_a?(Hash) && value.key?(:formula)) || value.is_a?(RichText) || value.is_a?(CellError))
    return EMPTY_ERRORS
  end

  errs = []
  errs << "row_index must be a non-negative Integer (got #{row_index.inspect})" if !row_index.is_a?(Integer) || row_index.negative?
  errs << "column_index must be a non-negative Integer (got #{column_index.inspect})" if !column_index.is_a?(Integer) || column_index.negative?
  errs << "row_index must be < 1048576 (got #{row_index}, max row is 1048575)" if row_index.is_a?(Integer) && row_index >= 1_048_576
  errs << "column_index must be < 16384 (got #{column_index}, max column is XFD=16383)" if column_index.is_a?(Integer) && column_index >= 16_384
  errs << "unsupported value type: #{value.class} (#{value.inspect}) — supported types: String, Numeric, true/false, Date, Time, or nil" unless value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false || value.is_a?(Date) || value.is_a?(Time)
  errs
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?

Parameters:

  • other (Object)

Returns:

  • (Object)


52
53
54
55
56
57
58
59
60
61
# File 'lib/xlsxrb/elements/cell.rb', line 52

def ==(other)
  other.is_a?(Cell) &&
    row_index == other.row_index &&
    column_index == other.column_index &&
    value == other.value &&
    formula == other.formula &&
    style_index == other.style_index &&
    unmapped_data == other.unmapped_data &&
    errors == other.errors
end

#[](key) ⇒ Object?

Access cell attributes by Symbol key.

: (Symbol key) -> untyped

Parameters:

  • key (Symbol)

    Attribute key (:value, :formula, :style_index, :ref, :column_index, :row_index, :type).

Returns:

  • (Object, nil)


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/xlsxrb/elements/cell.rb', line 113

def [](key)
  case key
  when :value then value
  when :formula
    formula.is_a?(Formula) ? formula.expression : formula
  when :style_index then style_index
  when :ref then ref
  when :column_index then column_index
  when :row_index then row_index
  when :type
    case value
    when String then "s"
    when true, false then "b"
    end
  end
end

#contentObject?

Returns the cell value.

: () -> untyped

Returns:

  • (Object, nil)


135
136
137
# File 'lib/xlsxrb/elements/cell.rb', line 135

def content
  value
end

#deconstructObject

Returns:

  • (Object)


68
69
70
# File 'lib/xlsxrb/elements/cell.rb', line 68

def deconstruct
  [row_index, column_index, value, formula, style_index, unmapped_data, errors]
end

#deconstruct_keys(keys) ⇒ Object

Parameters:

  • keys (Object)

Returns:

  • (Object)


72
73
74
75
76
# File 'lib/xlsxrb/elements/cell.rb', line 72

def deconstruct_keys(keys)
  h = { row_index: row_index, column_index: column_index, value: value, formula: formula,
        style_index: style_index, unmapped_data: unmapped_data, errors: errors }
  keys ? h.slice(*keys) : h
end

#hashObject

Returns:

  • (Object)


64
65
66
# File 'lib/xlsxrb/elements/cell.rb', line 64

def hash
  [row_index, column_index, value, formula, style_index, unmapped_data, errors].hash
end

#refString

Returns the Excel-style reference (e.g. "A1", "B2").

: () -> String

Returns:

  • (String)


103
104
105
# File 'lib/xlsxrb/elements/cell.rb', line 103

def ref
  "#{self.class.column_letter(column_index)}#{row_index + 1}"
end

#to_dateDate?

Converts the cell value (numeric serial date or date string) to Date.

: () -> Date?

Returns:

  • (Date, nil)


171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/xlsxrb/elements/cell.rb', line 171

def to_date
  return value if value.is_a?(Date)

  if value.is_a?(Numeric)
    Ooxml::Utils.serial_to_date(value)
  else
    begin
      Date.parse(value.to_s)
    rescue StandardError
      nil
    end
  end
end

#to_fFloat

Converts the cell value to Float.

: () -> Float

Returns:

  • (Float)


162
163
164
# File 'lib/xlsxrb/elements/cell.rb', line 162

def to_f
  value.to_f
end

#to_iInteger

Converts the cell value to Integer.

: () -> Integer

Returns:

  • (Integer)


153
154
155
# File 'lib/xlsxrb/elements/cell.rb', line 153

def to_i
  value.to_i
end

#to_sString

Returns the string representation of the cell value.

: () -> String

Returns:

  • (String)


144
145
146
# File 'lib/xlsxrb/elements/cell.rb', line 144

def to_s
  value.to_s
end

#to_timeTime?

Converts the cell value (numeric serial datetime or datetime string) to Time.

: () -> Time?

Returns:

  • (Time, nil)


190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/xlsxrb/elements/cell.rb', line 190

def to_time
  return value if value.is_a?(Time)

  if value.is_a?(Numeric)
    Ooxml::Utils.serial_to_datetime(value)
  else
    begin
      Time.parse(value.to_s)
    rescue StandardError
      nil
    end
  end
end

#valid?Boolean

Returns whether the cell is valid according to OOXML specifications.

: () -> bool

Returns:

  • (Boolean)


94
95
96
# File 'lib/xlsxrb/elements/cell.rb', line 94

def valid?
  errors.empty?
end

#with(**changes) ⇒ Object

Parameters:

  • changes (Object)

Returns:

  • (Object)


78
79
80
81
82
83
84
85
86
87
88
# File 'lib/xlsxrb/elements/cell.rb', line 78

def with(**changes)
  self.class.new(
    row_index: changes.fetch(:row_index, row_index),
    column_index: changes.fetch(:column_index, column_index),
    value: changes.fetch(:value, value),
    formula: changes.fetch(:formula, formula),
    style_index: changes.fetch(:style_index, style_index),
    unmapped_data: changes.fetch(:unmapped_data, unmapped_data),
    errors: changes.fetch(:errors, errors)
  )
end