Class: Xlsxrb::Elements::Worksheet

Inherits:
Data
  • Object
show all
Includes:
Enumerable
Defined in:
lib/xlsxrb/elements/worksheet.rb

Overview

Represents a single worksheet in a workbook.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, rows: [], columns: [], charts: [], unmapped_data: {}, errors: nil) ⇒ Worksheet

Returns a new instance of Worksheet.



11
12
13
14
15
# File 'lib/xlsxrb/elements/worksheet.rb', line 11

def initialize(name:, rows: [], columns: [], charts: [], unmapped_data: {}, errors: nil)
  computed_errors = errors || self.class.validate(name, rows)
  super(name: name, rows: rows.freeze, columns: columns.freeze, charts: charts.freeze,
        unmapped_data: unmapped_data, errors: computed_errors.freeze)
end

Instance Attribute Details

#chartsObject (readonly)

Returns the value of attribute charts

Returns:

  • (Object)

    the current value of charts



8
9
10
# File 'lib/xlsxrb/elements/worksheet.rb', line 8

def charts
  @charts
end

#columnsObject (readonly)

Returns the value of attribute columns

Returns:

  • (Object)

    the current value of columns



8
9
10
# File 'lib/xlsxrb/elements/worksheet.rb', line 8

def columns
  @columns
end

#errorsObject (readonly)

Returns the value of attribute errors

Returns:

  • (Object)

    the current value of errors



8
9
10
# File 'lib/xlsxrb/elements/worksheet.rb', line 8

def errors
  @errors
end

#nameObject (readonly)

Returns the value of attribute name

Returns:

  • (Object)

    the current value of name



8
9
10
# File 'lib/xlsxrb/elements/worksheet.rb', line 8

def name
  @name
end

#rowsObject (readonly)

Returns the value of attribute rows

Returns:

  • (Object)

    the current value of rows



8
9
10
# File 'lib/xlsxrb/elements/worksheet.rb', line 8

def rows
  @rows
end

#unmapped_dataObject (readonly)

Returns the value of attribute unmapped_data

Returns:

  • (Object)

    the current value of unmapped_data



8
9
10
# File 'lib/xlsxrb/elements/worksheet.rb', line 8

def unmapped_data
  @unmapped_data
end

Class Method Details

.validate(name, rows) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/xlsxrb/elements/worksheet.rb', line 127

def self.validate(name, rows)
  errs = []
  if name.nil? || !name.is_a?(String) || name.empty?
    errs << "worksheet name must be a non-empty String (got #{name.inspect})"
  else
    errs << "worksheet name cannot exceed 31 characters (got #{name.size})" if name.size > 31
    errs << "worksheet name cannot contain \\, /, ?, *, [, or ]" if name.match?(%r{[\\/?*\[\]]})
  end
  errs << "rows must be an Array (got #{rows.class})" unless rows.is_a?(Array)
  if rows.is_a?(Array)
    indices = rows.map(&:index)
    if indices.uniq.size != indices.size
      dups = indices.select { |i| indices.count(i) > 1 }.uniq
      errs << "duplicate row index: #{dups.join(", ")} — row indices within a sheet must be unique"
    end
  end
  errs
end

Instance Method Details

#[](ref) ⇒ Object



33
34
35
# File 'lib/xlsxrb/elements/worksheet.rb', line 33

def [](ref)
  cells_hash[ref.to_s.upcase]
end

#cell_value(ref) ⇒ Object

Returns cell value at Excel-style reference (e.g. "A1").



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/xlsxrb/elements/worksheet.rb', line 73

def cell_value(ref)
  parsed = Cell.parse_ref(ref)
  return nil unless parsed

  row_idx, col_idx = parsed
  row = row_at(row_idx)
  return nil unless row

  cell = row.cell_at(col_idx)
  cell&.value
end

#cellsObject



28
29
30
31
# File 'lib/xlsxrb/elements/worksheet.rb', line 28

def cells
  # Ensure ordered traversal
  cells_hash.values.sort_by { |c| [c.row_index, c.column_index] }
end

#cells_hashObject



17
18
19
20
21
22
23
24
25
26
# File 'lib/xlsxrb/elements/worksheet.rb', line 17

def cells_hash
  h = {}
  rows.each do |r|
    r.cells.each do |c|
      ref = "#{Cell.column_letter(c.column_index)}#{c.row_index + 1}"
      h[ref] = c
    end
  end
  h
end

#eachObject



37
38
39
40
41
# File 'lib/xlsxrb/elements/worksheet.rb', line 37

def each(&)
  return to_enum(:each) unless block_given?

  cells.each(&)
end

#each_cellObject



43
44
45
46
47
# File 'lib/xlsxrb/elements/worksheet.rb', line 43

def each_cell(&)
  return to_enum(:each_cell) unless block_given?

  cells.each(&)
end

#each_rowObject



49
50
51
52
53
# File 'lib/xlsxrb/elements/worksheet.rb', line 49

def each_row(&)
  return to_enum(:each_row) unless block_given?

  rows.each(&)
end

#first_rowObject



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

def first_row
  rows.min_by(&:index)
end

#last_rowObject



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

def last_row
  rows.max_by(&:index)
end

#row_at(index) ⇒ Object

Returns the row at the given 0-based index, or nil.



60
61
62
# File 'lib/xlsxrb/elements/worksheet.rb', line 60

def row_at(index)
  rows.find { |r| r.index == index }
end

#update_cell(ref, value: nil, style_index: nil, formula: nil) ⇒ Worksheet

Returns a new Worksheet with the specified cell updated.

Parameters:

  • ref (String)

    The cell reference (e.g. "B1").

  • value (Object) (defaults to: nil)

    The new cell value.

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

    Optional new style index.

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

    Optional new formula.

Returns:

Raises:

  • (ArgumentError)


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/xlsxrb/elements/worksheet.rb', line 92

def update_cell(ref, value: nil, style_index: nil, formula: nil)
  parsed = Cell.parse_ref(ref)
  raise ArgumentError, "invalid cell reference: #{ref}" unless parsed

  row_idx, col_idx = parsed
  existing_row = row_at(row_idx)

  if existing_row
    existing_cell = existing_row.cell_at(col_idx)
    new_cell = if existing_cell
                 existing_cell.with(
                   value: value || existing_cell.value,
                   style_index: style_index || existing_cell.style_index,
                   formula: formula || existing_cell.formula
                 )
               else
                 Cell.new(row_index: row_idx, column_index: col_idx, value: value, style_index: style_index, formula: formula)
               end

    # Replace cell in the existing row
    new_cells = existing_row.cells.reject { |c| c.column_index == col_idx }
    new_cells << new_cell
    new_cells.sort_by!(&:column_index)

    new_row = existing_row.with(cells: new_cells)
    new_rows = rows.map { |r| r.index == row_idx ? new_row : r }
  else
    # Row doesn't exist, create it
    new_cell = Cell.new(row_index: row_idx, column_index: col_idx, value: value, style_index: style_index, formula: formula)
    new_row = Row.new(index: row_idx, cells: [new_cell])
    new_rows = (rows + [new_row]).sort_by!(&:index)
  end
  with(rows: new_rows)
end

#valid?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/xlsxrb/elements/worksheet.rb', line 55

def valid?
  errors.empty?
end