Class: MarkdownServer::CsvBrowser::TableReader

Inherits:
Object
  • Object
show all
Defined in:
lib/markdown_server/csv_browser/table_reader.rb

Overview

Reads CSV files and applies view filtering (column subsets). Coerces values to match schema types (integer, number, string).

Instance Method Summary collapse

Constructor Details

#initialize(table) ⇒ TableReader

Returns a new instance of TableReader.



10
11
12
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 10

def initialize(table)
  @table = table
end

Instance Method Details

#delete_row(row_index) ⇒ Object

Deletes a row from the CSV file. Returns { deleted: true } or { deleted: false, error: “…” }.



70
71
72
73
74
75
76
77
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 70

def delete_row(row_index)
  rows = read_csv_raw
  return { deleted: false, error: "Row not found" } if row_index < 0 || row_index >= rows.length

  rows.delete_at(row_index)
  write_csv(rows)
  { deleted: true }
end

#duplicate_row(row_index) ⇒ Object

Duplicates a row by inserting an identical copy immediately after it. Returns { duplicated: true, new_index: row_index + 1 } or { duplicated: false, error: “…” }.



113
114
115
116
117
118
119
120
121
122
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 113

def duplicate_row(row_index)
  rows = read_csv_raw
  return { duplicated: false, error: "Row not found" } if row_index < 0 || row_index >= rows.length

  source = rows[row_index]
  copy = CSV::Row.new(source.headers, source.fields)
  rows.insert(row_index + 1, copy)
  write_csv(rows)
  { duplicated: true, new_index: row_index + 1 }
end

#insert_row(values, at: nil) ⇒ Object

Inserts a new row. ‘values` is { col_key => string_value }. When `at` is nil the row is appended; otherwise it’s inserted at that index. Returns { valid: true, new_index: i } or { valid: false, errors: […] }.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 82

def insert_row(values, at: nil)
  string_values = values.each_with_object({}) { |(k, v), h| h[k.to_s] = v.nil? ? nil : v.to_s }

  coerced = {}
  @table.columns.each do |col|
    val = coerce_for_validation(string_values[col.key], col.type)
    coerced[col.key] = val unless val.nil?
  end
  errors = @table.schema.validate(coerced).map { |e| format_error(e) }
  return { valid: false, errors: errors } unless errors.empty?

  rows = read_csv_raw
  headers = @table.columns.map(&:key)
  new_row = CSV::Row.new(headers, headers.map { |h| string_values[h] })

  if at.nil? || at >= rows.length
    rows << new_row
    new_index = rows.length - 1
  else
    at = 0 if at < 0
    rows.insert(at, new_row)
    new_index = at
  end

  write_csv(rows)
  { valid: true, new_index: new_index }
end

#read(view_key = nil) ⇒ Object

Returns { columns: [title, type], rows: [[idx, val, …]] } for the given view. Each row’s first element is its original file index.



16
17
18
19
20
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 16

def read(view_key = nil)
  view = find_view(view_key)
  all_data = read_csv
  apply_view(all_data, view)
end

#update_row(row_index, changes) ⇒ Object

Updates a row in the CSV file. changes is { col_key => new_string_value }. Returns { valid: true } or { valid: false, errors: […] }.



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 126

def update_row(row_index, changes)
  result = validate_cells(row_index, changes)
  return result unless result[:valid]

  rows = read_csv_raw
  row = rows[row_index]
  changes.each { |k, v| row[k] = v }

  write_csv(rows)
  { valid: true }
end

#validate_allObject

Validates all rows against the table schema. Returns { row_index => [error_strings, …], … } for failing rows only.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 49

def validate_all
  rows = read_csv_raw
  errors_by_row = {}

  rows.each_with_index do |row, idx|
    coerced = {}
    @table.columns.each do |col|
      val = coerce_for_validation(row[col.key], col.type)
      coerced[col.key] = val unless val.nil?
    end

    errors = @table.schema.validate(coerced).map { |e| format_error(e) }

    errors_by_row[idx] = errors unless errors.empty?
  end

  errors_by_row
end

#validate_cells(row_index, changes) ⇒ Object

Validates changed cells against the table schema. changes is a hash of { col_key => new_string_value }. Returns { valid: true } or { valid: false, errors: […] }.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/markdown_server/csv_browser/table_reader.rb', line 25

def validate_cells(row_index, changes)
  rows = read_csv_raw
  if row_index < 0 || row_index >= rows.length
    return { valid: false, errors: [{ "message" => "Row not found", "fields" => [] }] }
  end

  row = rows[row_index]
  merged = row.to_h.merge(changes)

  # Coerce to schema types for validation (skip blank optional fields
  # so json_schemer doesn't reject nil as non-string)
  coerced = {}
  @table.columns.each do |col|
    val = coerce_for_validation(merged[col.key], col.type)
    coerced[col.key] = val unless val.nil?
  end

  errors = @table.schema.validate(coerced).map { |e| format_error(e) }

  errors.empty? ? { valid: true } : { valid: false, errors: errors }
end